diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-11 02:37:39 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-11 02:37:39 -0600 |
| commit | 7ee008e150bc5bcf76082d726f719ee0fdfda982 (patch) | |
| tree | 27fb39be241fdb5ac2734c574de678977d1856d0 /libs/preview_bsa | |
Fluorine Manager: full Linux port of Mod Organizer 2
Complete native Linux port with FUSE-based virtual filesystem,
Proton/umu-run integration, and Flatpak packaging.
Key features:
- FUSE VFS replacing Windows USVFS (in-process + standalone helper for Flatpak)
- Proton/GE-Proton/umu-run launcher with env var forwarding
- Flatpak support (sandbox-aware VFS, NXM handler, umu-run)
- Wine prefix management UI
- Case-insensitive path resolution for Linux filesystems
- QSettings-safe INI handling (avoids Bethesda INI corruption)
- Portable instance support with auto-generated launcher scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'libs/preview_bsa')
| -rw-r--r-- | libs/preview_bsa/.clang-format | 41 | ||||
| -rw-r--r-- | libs/preview_bsa/.gitattributes | 7 | ||||
| -rw-r--r-- | libs/preview_bsa/.github/workflows/build.yml | 20 | ||||
| -rw-r--r-- | libs/preview_bsa/.github/workflows/linting.yml | 16 | ||||
| -rw-r--r-- | libs/preview_bsa/.gitignore | 5 | ||||
| -rw-r--r-- | libs/preview_bsa/.pre-commit-config.yaml | 20 | ||||
| -rw-r--r-- | libs/preview_bsa/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | libs/preview_bsa/CMakePresets.json | 67 | ||||
| -rw-r--r-- | libs/preview_bsa/src/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | libs/preview_bsa/src/preview_bsa_en.ts | 45 | ||||
| -rw-r--r-- | libs/preview_bsa/src/previewbsa.cpp | 234 | ||||
| -rw-r--r-- | libs/preview_bsa/src/previewbsa.h | 69 | ||||
| -rw-r--r-- | libs/preview_bsa/src/previewbsa.json | 1 | ||||
| -rw-r--r-- | libs/preview_bsa/src/simplefiletreeitem.cpp | 72 | ||||
| -rw-r--r-- | libs/preview_bsa/src/simplefiletreeitem.h | 32 | ||||
| -rw-r--r-- | libs/preview_bsa/src/simplefiletreemodel.cpp | 150 | ||||
| -rw-r--r-- | libs/preview_bsa/src/simplefiletreemodel.h | 48 | ||||
| -rw-r--r-- | libs/preview_bsa/vcpkg.json | 37 |
18 files changed, 881 insertions, 0 deletions
diff --git a/libs/preview_bsa/.clang-format b/libs/preview_bsa/.clang-format new file mode 100644 index 0000000..6098e1f --- /dev/null +++ b/libs/preview_bsa/.clang-format @@ -0,0 +1,41 @@ +--- +# We'll use defaults from the LLVM style, but with 4 columns indentation. +BasedOnStyle: LLVM +IndentWidth: 2 +--- +Language: Cpp +DeriveLineEnding: false +UseCRLF: true +DerivePointerAlignment: false +PointerAlignment: Left +AlignConsecutiveAssignments: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AlwaysBreakTemplateDeclarations: Yes +AccessModifierOffset: -2 +AlignTrailingComments: true +SpacesBeforeTrailingComments: 2 +NamespaceIndentation: Inner +MaxEmptyLinesToKeep: 1 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +ColumnLimit: 88 +ForEachMacros: ['Q_FOREACH', 'foreach'] diff --git a/libs/preview_bsa/.gitattributes b/libs/preview_bsa/.gitattributes new file mode 100644 index 0000000..f869712 --- /dev/null +++ b/libs/preview_bsa/.gitattributes @@ -0,0 +1,7 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.cpp text eol=crlf +*.h text eol=crlf diff --git a/libs/preview_bsa/.github/workflows/build.yml b/libs/preview_bsa/.github/workflows/build.yml new file mode 100644 index 0000000..030a354 --- /dev/null +++ b/libs/preview_bsa/.github/workflows/build.yml @@ -0,0 +1,20 @@ +name: Build Preview BSA + +on: + push: + branches: [master] + pull_request: + types: [opened, synchronize, reopened] + +env: + VCPKG_BINARY_SOURCES: clear;x-azblob,${{ vars.AZ_BLOB_VCPKG_URL }},${{ secrets.AZ_BLOB_SAS }},readwrite + +jobs: + build: + runs-on: windows-2022 + steps: + - name: Build Preview BSA + id: build-preview-bsa + uses: ModOrganizer2/build-with-mob-action@master + with: + mo2-dependencies: uibase bsatk diff --git a/libs/preview_bsa/.github/workflows/linting.yml b/libs/preview_bsa/.github/workflows/linting.yml new file mode 100644 index 0000000..8de4aa2 --- /dev/null +++ b/libs/preview_bsa/.github/workflows/linting.yml @@ -0,0 +1,16 @@ +name: Lint Preview BSA Plugin + +on: + push: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check format + uses: ModOrganizer2/check-formatting-action@master + with: + check-path: "." diff --git a/libs/preview_bsa/.gitignore b/libs/preview_bsa/.gitignore new file mode 100644 index 0000000..cf71be7 --- /dev/null +++ b/libs/preview_bsa/.gitignore @@ -0,0 +1,5 @@ +edit +CMakeLists.txt.user +/msbuild.log +/*std*.log +/*build diff --git a/libs/preview_bsa/.pre-commit-config.yaml b/libs/preview_bsa/.pre-commit-config.yaml new file mode 100644 index 0000000..eb8969c --- /dev/null +++ b/libs/preview_bsa/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-merge-conflict + - id: check-case-conflict + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v21.1.8 + hooks: + - id: clang-format + 'types_or': [c++, c] + +ci: + autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks." + autofix_prs: true + autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate." + autoupdate_schedule: quarterly + submodules: false diff --git a/libs/preview_bsa/CMakeLists.txt b/libs/preview_bsa/CMakeLists.txt new file mode 100644 index 0000000..e7757aa --- /dev/null +++ b/libs/preview_bsa/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.16) + +project(preview_bsa) +add_subdirectory(src) diff --git a/libs/preview_bsa/CMakePresets.json b/libs/preview_bsa/CMakePresets.json new file mode 100644 index 0000000..5a63c77 --- /dev/null +++ b/libs/preview_bsa/CMakePresets.json @@ -0,0 +1,67 @@ +{ + "configurePresets": [ + { + "errors": { + "deprecated": true + }, + "hidden": true, + "name": "cmake-dev", + "warnings": { + "deprecated": true, + "dev": true + } + }, + { + "cacheVariables": { + "VCPKG_MANIFEST_NO_DEFAULT_FEATURES": { + "type": "BOOL", + "value": "ON" + } + }, + "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "hidden": true, + "name": "vcpkg" + }, + { + "hidden": true, + "inherits": ["vcpkg"], + "name": "vcpkg-dev" + }, + { + "binaryDir": "${sourceDir}/vsbuild", + "architecture": { + "strategy": "set", + "value": "x64" + }, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4", + "VCPKG_TARGET_TRIPLET": { + "type": "STRING", + "value": "x64-windows-static-md" + } + }, + "generator": "Visual Studio 17 2022", + "inherits": ["cmake-dev", "vcpkg-dev"], + "name": "vs2022-windows", + "toolset": "v143" + }, + { + "cacheVariables": { + "VCPKG_MANIFEST_FEATURES": { + "type": "STRING", + "value": "standalone" + } + }, + "inherits": "vs2022-windows", + "name": "vs2022-windows-standalone" + } + ], + "buildPresets": [ + { + "name": "vs2022-windows", + "resolvePackageReferences": "on", + "configurePreset": "vs2022-windows" + } + ], + "version": 4 +} diff --git a/libs/preview_bsa/src/CMakeLists.txt b/libs/preview_bsa/src/CMakeLists.txt new file mode 100644 index 0000000..fe85679 --- /dev/null +++ b/libs/preview_bsa/src/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.16) + +file(GLOB preview_bsa_SOURCES CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/*.ui + ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc +) + +add_library(preview_bsa SHARED ${preview_bsa_SOURCES}) +mo2_configure_plugin(preview_bsa NO_SOURCES WARNINGS 4) +target_link_libraries(preview_bsa PRIVATE mo2::uibase mo2::bsatk) +mo2_install_plugin(preview_bsa) diff --git a/libs/preview_bsa/src/preview_bsa_en.ts b/libs/preview_bsa/src/preview_bsa_en.ts new file mode 100644 index 0000000..0a836bc --- /dev/null +++ b/libs/preview_bsa/src/preview_bsa_en.ts @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="en_US"> +<context> + <name>PreviewBsa</name> + <message> + <location filename="previewbsa.cpp" line="64"/> + <source>Preview BSA</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="previewbsa.cpp" line="74"/> + <source>Supports previewing contents of Bethesda Archive files, BSAs and BA2s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="previewbsa.cpp" line="86"/> + <source>Enable previewing of BSA file contents</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="previewbsa.cpp" line="147"/> + <source>Archive Format: %1 , Compression: %2 , File count: %3 , Version: %4 , Archive type: %5 , Archive flags: %6</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="previewbsa.cpp" line="152"/> + <source>yes</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="previewbsa.cpp" line="152"/> + <source>no</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> + <name>SimpleFileTreeModel</name> + <message> + <location filename="simplefiletreemodel.cpp" line="22"/> + <source>File Name</source> + <translation type="unfinished"></translation> + </message> +</context> +</TS> diff --git a/libs/preview_bsa/src/previewbsa.cpp b/libs/preview_bsa/src/previewbsa.cpp new file mode 100644 index 0000000..e21df2f --- /dev/null +++ b/libs/preview_bsa/src/previewbsa.cpp @@ -0,0 +1,234 @@ +/* +Copyright (C) 2014 Sebastian Herbord. All rights reserved. + +This file is part of Bsa Preview plugin for MO + +Bsa Preview plugin is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Bsa Preview plugin is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Bsa Preview plugin. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "previewbsa.h" + +#include <QApplication> +#include <QFileInfo> +#include <QImageReader> +#include <QLabel> +#include <QLineEdit> +#include <QScreen> +#include <QStandardItemModel> +#include <QTextEdit> +#include <QTreeView> +#include <QTreeWidget> +#include <QVBoxLayout> +#include <QtPlugin> + +#include <uibase/filterwidget.h> +#include <uibase/log.h> +#include <uibase/utility.h> + +#include "simplefiletreemodel.h" + +using namespace MOBase; + +PreviewBsa::PreviewBsa() : m_MOInfo(nullptr) {} + +bool PreviewBsa::init(IOrganizer* moInfo) +{ + m_MOInfo = moInfo; + + auto bsaPreview = std::bind(&PreviewBsa::genBsaPreview, this, std::placeholders::_1, + std::placeholders::_2); + + m_PreviewGenerators["bsa"] = bsaPreview; + m_PreviewGenerators["ba2"] = bsaPreview; + + return true; +} + +QString PreviewBsa::name() const +{ + return "Preview Bsa"; +} + +QString PreviewBsa::localizedName() const +{ + return tr("Preview BSA"); +} + +QString PreviewBsa::author() const +{ + return "AL12 & MO2 Team"; +} + +QString PreviewBsa::description() const +{ + return tr("Supports previewing contents of Bethesda Archive files, BSAs and BA2s"); +} + +MOBase::VersionInfo PreviewBsa::version() const +{ + return VersionInfo(1, 1, 0, VersionInfo::RELEASE_FINAL); +} + +QList<MOBase::PluginSetting> PreviewBsa::settings() const +{ + QList<PluginSetting> result; + result.push_back(PluginSetting( + "enabled", tr("Enable previewing of BSA file contents"), QVariant(true))); + return result; +} + +std::set<QString> PreviewBsa::supportedExtensions() const +{ + std::set<QString> extensions; + for (const auto& generator : m_PreviewGenerators) { + extensions.insert(generator.first); + } + + return extensions; +} + +QWidget* PreviewBsa::genFilePreview(const QString& fileName, const QSize& maxSize) const +{ + auto iter = m_PreviewGenerators.find(QFileInfo(fileName).suffix().toLower()); + if (iter != m_PreviewGenerators.end()) { + return iter->second(fileName, maxSize); + } else { + return nullptr; + } +} + +void PreviewBsa::readFiles(const BSA::Folder::Ptr archiveFolder) +{ + const auto fileCount = archiveFolder->getNumFiles(); + for (unsigned int i = 0; i < fileCount; ++i) { + const BSA::File::Ptr file = archiveFolder->getFile(i); + + m_Files << QString::fromStdString(file->getFilePath()); + } + + // recurse into subdirectories + const auto dirCount = archiveFolder->getNumSubFolders(); + for (unsigned int i = 0; i < dirCount; ++i) { + const BSA::Folder::Ptr folder = archiveFolder->getSubFolder(i); + + readFiles(folder); + } +} + +QWidget* PreviewBsa::genBsaPreview(const QString& fileName, const QSize&) +{ + m_Files.clear(); + QWidget* wrapper = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(); + + QLabel* infoLabel = new QLabel(); + BSA::Archive arch; // bs_archive_auto is easier to use, but is less performant when + // working with memory + BSA::EErrorCode res = arch.read(fileName.toLocal8Bit().constData(), true); + if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) { + log::error("invalid bsa '{}', error {}", fileName, res); + infoLabel->setText("Unable to parse archive. Unrecognized format."); + arch.close(); + return wrapper; + } + const BSA::Folder::Ptr archiveDir = arch.getRoot(); + readFiles(archiveDir); + QString infoString = + tr("Archive Format: %1 , Compression: %2 , File count: %3 , Version: %4 , " + "Archive type: %5 , Archive flags: %6"); + // tr("Archive Format: %1 , Compression: %2 , File count: %3 , Version: %4 , " + // "Archive type: %5 , Archive flags: %6 , Contents flags: %7"); + infoString = infoString.arg(getFormat(arch.getType())) + .arg((arch.getFlags() & 0x00000004) ? tr("yes") : tr("no")) + .arg(m_Files.size()) + .arg(getVersion(arch.getType())) + .arg(arch.getType()) + .arg("0x" + QString::number(arch.getFlags(), 16)); + //.arg("0x" + QString::number(arch.get_file_flags(), 16)); + infoLabel->setText(infoString); + layout->addWidget(infoLabel); + + QTreeView* view = new QTreeView(); + SimpleFileTreeModel* model = new SimpleFileTreeModel(m_Files, view); + + view->setModel(model); + layout->addWidget(view); + + QLineEdit* lineEdit = new QLineEdit(); + layout->addWidget(lineEdit); + + model->setFilterWidgetEdit(lineEdit); + model->setFilterWidgetList(view); + + view->setSortingEnabled(true); + view->sortByColumn(0, Qt::SortOrder::AscendingOrder); + + wrapper->setLayout(layout); + arch.close(); + return wrapper; +} + +QString PreviewBsa::getFormat(ArchiveType type) const +{ + switch (type) { + case ArchiveType::TYPE_MORROWIND: + return "Morrowind"; + case ArchiveType::TYPE_OBLIVION: + return "Oblivion"; + case ArchiveType::TYPE_FALLOUT3: + return "Fallout 3, New Vegas, Skyrim LE"; + case ArchiveType::TYPE_SKYRIMSE: + return "Skyrim Special Edition"; + case ArchiveType::TYPE_FALLOUT4: + return "Fallout 4"; + case ArchiveType::TYPE_STARFIELD: + return "Starfield"; + case ArchiveType::TYPE_STARFIELD_LZ4_TEXTURE: + return "Starfield DDS LZ4"; + case ArchiveType::TYPE_FALLOUT4NG_7: + case ArchiveType::TYPE_FALLOUT4NG_8: + return "Fallout 4 NextGen"; + default: + throw data_invalid_exception(makeString("invalid type %d", type)); + } +} + +BSAULong PreviewBsa::getVersion(ArchiveType type) const +{ + switch (type) { + case TYPE_MORROWIND: + return 0x100; + case TYPE_OBLIVION: + return 0x67; + case TYPE_FALLOUT3: + return 0x68; + case TYPE_SKYRIMSE: + return 0x69; + case TYPE_FALLOUT4: + return 0x01; + case TYPE_STARFIELD: + return 0x02; + case TYPE_STARFIELD_LZ4_TEXTURE: + return 0x03; + case TYPE_FALLOUT4NG_7: + return 0x07; + case TYPE_FALLOUT4NG_8: + return 0x08; + default: + throw data_invalid_exception(makeString("invalid type %d", type)); + } +} + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +Q_EXPORT_PLUGIN2(previewBsa, PreviewBsa) +#endif diff --git a/libs/preview_bsa/src/previewbsa.h b/libs/preview_bsa/src/previewbsa.h new file mode 100644 index 0000000..6c3ee2e --- /dev/null +++ b/libs/preview_bsa/src/previewbsa.h @@ -0,0 +1,69 @@ +/* +Copyright (C) 2014 Sebastian Herbord. All rights reserved. + +This file is part of bsa Preview plugin for MO + +bsa Preview plugin is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +bsa Preview plugin is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with bsa Preview plugin. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PREVIEWBSA_H +#define PREVIEWBSA_H + +#include <functional> + +#include <bsatk/bsatk.h> + +#include <uibase/ipluginpreview.h> + +class PreviewBsa : public MOBase::IPluginPreview +{ + + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + Q_PLUGIN_METADATA(IID "org.tannin.PreviewBsa" FILE "previewbsa.json") +#endif + +public: + PreviewBsa(); + +public: + virtual bool init(MOBase::IOrganizer* moInfo); + virtual QString name() const; + virtual QString localizedName() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual QList<MOBase::PluginSetting> settings() const; + +public: + virtual std::set<QString> supportedExtensions() const; + virtual QWidget* genFilePreview(const QString& fileName, const QSize& maxSize) const; + +private: + void readFiles(const BSA::Folder::Ptr folder); + QWidget* genBsaPreview(const QString& fileName, const QSize& maxSize); + QString getFormat(ArchiveType type) const; + BSAULong getVersion(ArchiveType type) const; + +private: + std::map<QString, std::function<QWidget*(const QString&, const QSize&)>> + m_PreviewGenerators; + +private: + const MOBase::IOrganizer* m_MOInfo; + QStringList m_Files; +}; + +#endif // PREVIEWBSA_H diff --git a/libs/preview_bsa/src/previewbsa.json b/libs/preview_bsa/src/previewbsa.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/libs/preview_bsa/src/previewbsa.json @@ -0,0 +1 @@ +{} diff --git a/libs/preview_bsa/src/simplefiletreeitem.cpp b/libs/preview_bsa/src/simplefiletreeitem.cpp new file mode 100644 index 0000000..5bcc5d4 --- /dev/null +++ b/libs/preview_bsa/src/simplefiletreeitem.cpp @@ -0,0 +1,72 @@ + + +/* + simplefiletreeitem.cpp + + A container for items of data supplied by the simple file tree model. +*/ + +#include "simplefiletreeitem.h" + +SimpleFileTreeItem::SimpleFileTreeItem(const QVector<QVariant>& data, + SimpleFileTreeItem* parent) + : m_itemData(data), m_parentItem(parent) +{} + +SimpleFileTreeItem::~SimpleFileTreeItem() +{ + qDeleteAll(m_childItems); +} + +void SimpleFileTreeItem::appendChild(const QString& name, SimpleFileTreeItem* item) +{ + m_childItems.append(item); + m_childItemsByName.insert(name, item); +} + +SimpleFileTreeItem* SimpleFileTreeItem::child(int row) +{ + if (row < 0 || row >= m_childItems.size()) + return nullptr; + return m_childItems.at(row); +} + +SimpleFileTreeItem* SimpleFileTreeItem::childByName(const QString& name) +{ + return m_childItemsByName.value(name); +} + +QVector<SimpleFileTreeItem*> SimpleFileTreeItem::children() +{ + return m_childItems; +} + +int SimpleFileTreeItem::childCount() const +{ + return m_childItems.count(); +} + +int SimpleFileTreeItem::columnCount() const +{ + return m_itemData.count(); +} + +QVariant SimpleFileTreeItem::data(int column) const +{ + if (column < 0 || column >= m_itemData.size()) + return QVariant(); + return m_itemData.at(column); +} + +SimpleFileTreeItem* SimpleFileTreeItem::parentItem() +{ + return m_parentItem; +} + +int SimpleFileTreeItem::row() const +{ + if (m_parentItem) + return m_parentItem->m_childItems.indexOf(const_cast<SimpleFileTreeItem*>(this)); + + return 0; +} diff --git a/libs/preview_bsa/src/simplefiletreeitem.h b/libs/preview_bsa/src/simplefiletreeitem.h new file mode 100644 index 0000000..3d5ddd7 --- /dev/null +++ b/libs/preview_bsa/src/simplefiletreeitem.h @@ -0,0 +1,32 @@ +#ifndef SIMPLEFILETREEITEM_H +#define SIMPLEFILETREEITEM_H + +#include <QVariant> +#include <QVector> + +class SimpleFileTreeItem +{ +public: + explicit SimpleFileTreeItem(const QVector<QVariant>& data, + SimpleFileTreeItem* parentItem = nullptr); + ~SimpleFileTreeItem(); + + void appendChild(const QString& name, SimpleFileTreeItem* child); + + SimpleFileTreeItem* child(int row); + SimpleFileTreeItem* childByName(const QString& name); + QVector<SimpleFileTreeItem*> children(); + int childCount() const; + int columnCount() const; + QVariant data(int column) const; + int row() const; + SimpleFileTreeItem* parentItem(); + +private: + QVector<SimpleFileTreeItem*> m_childItems; + QHash<QString, SimpleFileTreeItem*> m_childItemsByName; + QVector<QVariant> m_itemData; + SimpleFileTreeItem* m_parentItem; +}; + +#endif // SIMPLEFILETREEITEM_H diff --git a/libs/preview_bsa/src/simplefiletreemodel.cpp b/libs/preview_bsa/src/simplefiletreemodel.cpp new file mode 100644 index 0000000..46dcaf7 --- /dev/null +++ b/libs/preview_bsa/src/simplefiletreemodel.cpp @@ -0,0 +1,150 @@ + +/* + simplefiletreemodel.cpp + + Provides a simple tree model for files and folders +*/ + +#include "simplefiletreemodel.h" +#include "simplefiletreeitem.h" + +#include <QDir> +#include <QFileIconProvider> +#include <QStringList> + +SimpleFileTreeModel::SimpleFileTreeModel(const QStringList& data, QObject* parent) + : QAbstractItemModel(parent) +{ + QFileIconProvider* provider = new QFileIconProvider(); + m_FolderIcon = provider->icon(QFileIconProvider::Folder); + m_FileIcon = provider->icon(QFileIconProvider::File); + delete provider; + m_RootItem = new SimpleFileTreeItem({tr("File Name")}); + setupModelData(data, m_RootItem); +} + +SimpleFileTreeModel::~SimpleFileTreeModel() +{ + delete m_RootItem; +} + +int SimpleFileTreeModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + return static_cast<SimpleFileTreeItem*>(parent.internalPointer())->columnCount(); + return m_RootItem->columnCount(); +} + +QVariant SimpleFileTreeModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + SimpleFileTreeItem* item = static_cast<SimpleFileTreeItem*>(index.internalPointer()); + + if (role == Qt::DecorationRole) { + if (item->childCount() > 0) { + return m_FolderIcon; + } else { + return m_FileIcon; + } + } + + if (role != Qt::DisplayRole) + return QVariant(); + + return item->data(index.column()); +} + +Qt::ItemFlags SimpleFileTreeModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return QAbstractItemModel::flags(index); +} + +QVariant SimpleFileTreeModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return m_RootItem->data(section); + + return QVariant(); +} + +QModelIndex SimpleFileTreeModel::index(int row, int column, + const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + SimpleFileTreeItem* parentItem; + + if (!parent.isValid()) + parentItem = m_RootItem; + else + parentItem = static_cast<SimpleFileTreeItem*>(parent.internalPointer()); + + SimpleFileTreeItem* childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + return QModelIndex(); +} + +QModelIndex SimpleFileTreeModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + SimpleFileTreeItem* childItem = + static_cast<SimpleFileTreeItem*>(index.internalPointer()); + SimpleFileTreeItem* parentItem = childItem->parentItem(); + + if (parentItem == m_RootItem) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem); +} + +int SimpleFileTreeModel::rowCount(const QModelIndex& parent) const +{ + SimpleFileTreeItem* parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = m_RootItem; + else + parentItem = static_cast<SimpleFileTreeItem*>(parent.internalPointer()); + + return parentItem->childCount(); +} + +void SimpleFileTreeModel::setupModelData(const QStringList& lines, SimpleFileTreeItem*) +{ + for (QString line : lines) { + auto fullPath = QDir::cleanPath(line); + QStringList lineEntries = fullPath.split("/"); + auto currentParent = m_RootItem; + + for (int i = 0; i < lineEntries.count(); i++) { + QString currentEntryName = lineEntries[i]; + + // check if item was already added + SimpleFileTreeItem* currentEntry = currentParent->childByName(currentEntryName); + + // add tree item if not found + if (currentEntry == nullptr) { + QVector<QVariant> columnData; + columnData.reserve(m_ColumnCount); + columnData << currentEntryName; + currentEntry = new SimpleFileTreeItem(columnData, currentParent); + currentParent->appendChild(currentEntryName, currentEntry); + } + + // as we go deeper into the path + currentParent = currentEntry; + } + } +} diff --git a/libs/preview_bsa/src/simplefiletreemodel.h b/libs/preview_bsa/src/simplefiletreemodel.h new file mode 100644 index 0000000..5189d89 --- /dev/null +++ b/libs/preview_bsa/src/simplefiletreemodel.h @@ -0,0 +1,48 @@ +#ifndef SIMPLEFILETREEMODEL_H +#define SIMPLEFILETREEMODEL_H + +#include <QAbstractItemModel> +#include <QIcon> +#include <QLineEdit> +#include <QModelIndex> +#include <QVariant> + +#include <uibase/filterwidget.h> + +using namespace MOBase; + +class SimpleFileTreeItem; + +class SimpleFileTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit SimpleFileTreeModel(const QStringList& data, QObject* parent = nullptr); + ~SimpleFileTreeModel(); + + void setFilterWidgetList(QAbstractItemView* list) { m_FilterWidget.setList(list); } + void setFilterWidgetEdit(QLineEdit* edit) { m_FilterWidget.setEdit(edit); } + + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, + const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + +private: + // takes a list of relative file paths assumed with the same base folder and generates + // the filetree model + void setupModelData(const QStringList& lines, SimpleFileTreeItem* parent); + const int m_ColumnCount = 1; + SimpleFileTreeItem* m_RootItem; + FilterWidget m_FilterWidget; + QIcon m_FileIcon; + QIcon m_FolderIcon; +}; + +#endif // SIMPLEFILETREEMODEL_H diff --git a/libs/preview_bsa/vcpkg.json b/libs/preview_bsa/vcpkg.json new file mode 100644 index 0000000..ecb6d49 --- /dev/null +++ b/libs/preview_bsa/vcpkg.json @@ -0,0 +1,37 @@ +{ + "dependencies": [ + "mo2-dds-header", + "zlib", + "lz4", + "mo2-dds-header", + "boost-interprocess", + "boost-thread" + ], + "features": { + "standalone": { + "description": "Build Standalone.", + "dependencies": ["mo2-cmake", "mo2-uibase", "mo2-bsatk"] + } + }, + "vcpkg-configuration": { + "default-registry": { + "kind": "git", + "repository": "https://github.com/Microsoft/vcpkg", + "baseline": "294f76666c3000630d828703e675814c05a4fd43" + }, + "registries": [ + { + "kind": "git", + "repository": "https://github.com/Microsoft/vcpkg", + "baseline": "294f76666c3000630d828703e675814c05a4fd43", + "packages": ["boost*", "boost-*"] + }, + { + "kind": "git", + "repository": "https://github.com/ModOrganizer2/vcpkg-registry", + "baseline": "8beb2e0efa9c17dd6d17bb05288dd1e40727f673", + "packages": ["mo2-*", "pybind11", "spdlog"] + } + ] + } +} |
