diff options
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/datatab.cpp | 173 | ||||
| -rw-r--r-- | src/datatab.h | 4 | ||||
| -rw-r--r-- | src/filetree.cpp | 396 | ||||
| -rw-r--r-- | src/filetree.h | 110 | ||||
| -rw-r--r-- | src/mainwindow.ui | 14 |
6 files changed, 540 insertions, 160 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0658da31..ad791e79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,7 @@ SET(organizer_SRCS lootdialog.cpp filterlist.cpp datatab.cpp + filetree.cpp shared/windows_error.cpp shared/error_report.cpp @@ -270,6 +271,7 @@ SET(organizer_HDRS json.h filterlist.h datatab.h + filetree.h shared/windows_error.h shared/error_report.h @@ -401,6 +403,7 @@ set(loot set(mainwindow datatab + filetree filterlist mainwindow statusbar diff --git a/src/datatab.cpp b/src/datatab.cpp index fb42a904..bc0ff455 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -4,6 +4,7 @@ #include "organizercore.h" #include "directoryentry.h" #include "messagedialog.h" +#include "filetree.h" #include <log.h> #include <report.h> @@ -18,21 +19,24 @@ DataTab::DataTab( OrganizerCore& core, PluginContainer& pc, QWidget* parent, Ui::MainWindow* mwui) : m_core(core), m_pluginContainer(pc), m_archives(false), m_parent(parent), + m_model(new FileTreeModel(core)), ui{ mwui->btnRefreshData, mwui->dataTree, mwui->conflictsCheckBox, mwui->showArchiveDataCheckBox} { + ui.tree->setModel(m_model); + connect( ui.refresh, &QPushButton::clicked, [&]{ onRefresh(); }); - connect( - ui.tree, &QTreeWidget::itemExpanded, - [&](auto* item){ onItemExpanded(item); }); - - connect( - ui.tree, &QTreeWidget::itemActivated, - [&](auto* item, int col){ onItemActivated(item, col); }); + //connect( + // ui.tree, &QTreeWidget::itemExpanded, + // [&](auto* item){ onItemExpanded(item); }); + // + //connect( + // ui.tree, &QTreeWidget::itemActivated, + // [&](auto* item, int col){ onItemActivated(item, col); }); connect( ui.tree, &QTreeWidget::customContextMenuRequested, @@ -64,12 +68,13 @@ void DataTab::activated() QTreeWidgetItem* DataTab::singleSelection() { - const auto sel = ui.tree->selectedItems(); - if (sel.count() != 1) { - return nullptr; - } - - return sel[0]; + //const auto sel = ui.tree->selectedItems(); + //if (sel.count() != 1) { + // return nullptr; + //} + // + //return sel[0]; + return nullptr; } void DataTab::openSelection() @@ -144,21 +149,16 @@ void DataTab::onRefresh() void DataTab::refreshDataTree() { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - ui.tree->clear(); - QStringList columns("data"); - columns.append(""); - QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); - subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_core.directoryStructure(), ui.conflicts->isChecked(), &fileIcon, &folderIcon); - ui.tree->insertTopLevelItem(0, subTree); - subTree->setExpanded(true); + m_model->refresh(); + ui.tree->expand(m_model->index(0, 0)); } void DataTab::refreshDataTreeKeepExpandedNodes() { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + //m_model->refreshKeepExpandedNodes(); + m_model->refresh(); // temp + + /*QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); QStringList expandedNodes; QTreeWidgetItemIterator it1(ui.tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren); @@ -185,7 +185,7 @@ void DataTab::refreshDataTreeKeepExpandedNodes() current->setExpanded(true); } ++it2; - } + }*/ } void DataTab::updateTo( @@ -193,127 +193,6 @@ void DataTab::updateTo( const DirectoryEntry &directoryEntry, bool conflictsOnly, QIcon *fileIcon, QIcon *folderIcon) { - bool isDirectory = true; - //QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - //QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - - std::wostringstream temp; - temp << directorySoFar << "\\" << directoryEntry.getName(); - { - std::vector<DirectoryEntry*>::const_iterator current, end; - directoryEntry.getSubDirectories(current, end); - for (; current != end; ++current) { - QString pathName = QString::fromStdWString((*current)->getName()); - QStringList columns(pathName); - columns.append(""); - if (!(*current)->isEmpty()) { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, *folderIcon); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - - if (conflictsOnly || !m_archives) { - updateTo(directoryChild, temp.str(), **current, conflictsOnly, fileIcon, folderIcon); - if (directoryChild->childCount() != 0) { - subTree->addChild(directoryChild); - } - else { - delete directoryChild; - } - } - else { - QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList()); - onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__"); - onDemandLoad->setData(0, Qt::UserRole + 1, QString::fromStdWString(temp.str())); - onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly); - directoryChild->addChild(onDemandLoad); - subTree->addChild(directoryChild); - } - } - else { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, *folderIcon); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - subTree->addChild(directoryChild); - } - } - } - - - isDirectory = false; - { - for (const FileEntry::Ptr current : directoryEntry.getFiles()) { - if (conflictsOnly && (current->getAlternatives().size() == 0)) { - continue; - } - - bool isArchive = false; - int originID = current->getOrigin(isArchive); - if (!m_archives && isArchive) { - continue; - } - - QString fileName = QString::fromStdWString(current->getName()); - QStringList columns(fileName); - FilesOrigin origin = m_core.directoryStructure()->getOriginByID(originID); - - QString source; - const unsigned int modIndex = ModInfo::getIndex( - QString::fromStdWString(origin.getName())); - - if (modIndex == UINT_MAX) { - source = UnmanagedModName(); - } else { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - source = modInfo->name(); - } - - std::pair<std::wstring, int> archive = current->getArchive(); - if (archive.first.length() != 0) { - source.append(" (").append(QString::fromStdWString(archive.first)).append(")"); - } - columns.append(source); - QTreeWidgetItem *fileChild = new QTreeWidgetItem(columns); - if (isArchive) { - QFont font = fileChild->font(0); - font.setItalic(true); - fileChild->setFont(0, font); - fileChild->setFont(1, font); - } else if (fileName.endsWith(ModInfo::s_HiddenExt)) { - QFont font = fileChild->font(0); - font.setStrikeOut(true); - fileChild->setFont(0, font); - fileChild->setFont(1, font); - } - fileChild->setData(0, Qt::UserRole, QString::fromStdWString(current->getFullPath())); - fileChild->setData(0, Qt::DecorationRole, *fileIcon); - fileChild->setData(0, Qt::UserRole + 3, isDirectory); - fileChild->setData(0, Qt::UserRole + 1, isArchive); - fileChild->setData(1, Qt::UserRole, source); - fileChild->setData(1, Qt::UserRole + 1, originID); - - std::vector<std::pair<int, std::pair<std::wstring, int>>> alternatives = current->getAlternatives(); - - if (!alternatives.empty()) { - std::wostringstream altString; - altString << tr("Also in: <br>").toStdWString(); - for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator altIter = alternatives.begin(); - altIter != alternatives.end(); ++altIter) { - if (altIter != alternatives.begin()) { - altString << " , "; - } - altString << "<span style=\"white-space: nowrap;\"><i>" << m_core.directoryStructure()->getOriginByID(altIter->first).getName() << "</font></span>"; - } - fileChild->setToolTip(1, QString("%1").arg(QString::fromStdWString(altString.str()))); - fileChild->setForeground(1, QBrush(Qt::red)); - } else { - fileChild->setToolTip(1, tr("No conflict")); - } - subTree->addChild(fileChild); - } - } - - - //subTree->sortChildren(0, Qt::AscendingOrder); } void DataTab::onItemExpanded(QTreeWidgetItem* item) @@ -369,7 +248,7 @@ void DataTab::onItemActivated(QTreeWidgetItem *item, int column) void DataTab::onContextMenu(const QPoint &pos) { - auto* item = ui.tree->itemAt(pos.x(), pos.y()); + QTreeWidgetItem* item = nullptr;//ui.tree->itemAt(pos.x(), pos.y()); QMenu menu; if ((item != nullptr) && (item->childCount() == 0) diff --git a/src/datatab.h b/src/datatab.h index 27d36936..8754c12d 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -8,6 +8,7 @@ namespace Ui { class MainWindow; } class OrganizerCore; class Settings; class PluginContainer; +class FileTreeModel; namespace MOShared { class DirectoryEntry; } @@ -36,7 +37,7 @@ private: struct DataTabUi { QPushButton* refresh; - QTreeWidget* tree; + QTreeView* tree; QCheckBox* conflicts; QCheckBox* archives; }; @@ -45,6 +46,7 @@ private: PluginContainer& m_pluginContainer; bool m_archives; QWidget* m_parent; + FileTreeModel* m_model; DataTabUi ui; std::vector<QTreeWidgetItem*> m_removeLater; diff --git a/src/filetree.cpp b/src/filetree.cpp new file mode 100644 index 00000000..cfc73de0 --- /dev/null +++ b/src/filetree.cpp @@ -0,0 +1,396 @@ +#include "filetree.h" +#include "organizercore.h" + +using namespace MOShared; + +// 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<FileTreeItem> child) +{ + m_children.push_back(std::move(child)); +} + +const std::vector<std::unique_ptr<FileTreeItem>>& FileTreeItem::children() const +{ + return m_children; +} + +FileTreeItem* FileTreeItem::parent() +{ + return m_parent; +} + +const QString& FileTreeItem::filename() const +{ + return m_file; +} + +const QString& FileTreeItem::mod() const +{ + return m_mod; +} + +bool FileTreeItem::isFromArchive() const +{ + return (m_flags & FromArchive); +} + +bool FileTreeItem::isHidden() const +{ + return m_file.endsWith(ModInfo::s_HiddenExt); +} + +bool FileTreeItem::isConflicted() const +{ + return (m_flags & Conflicted); +} + +QFileIconProvider::IconType FileTreeItem::icon() const +{ + if (m_flags & Directory) { + return QFileIconProvider::Folder; + } else { + return QFileIconProvider::File; + } +} + +void FileTreeItem::setLoaded(bool b) +{ + m_loaded = b; +} + + + +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() +{ + m_root = {nullptr, L"", L"", FileTreeItem::Directory, L"Data", L""}; + + fill(m_root, *m_core.directoryStructure(), L""); + m_root.setLoaded(true); +} + +void FileTreeModel::fill( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath) +{ + const std::wstring path = parentPath + L"\\" + parentEntry.getName(); + bool isDirectory = true; + + + { + std::vector<DirectoryEntry*>::const_iterator begin, end; + parentEntry.getSubDirectories(begin, end); + fillDirectories(parentItem, path, begin, end); + } + + { + fillFiles(parentItem, path, parentEntry.getFiles()); + } +} + +void FileTreeModel::fillDirectories( + FileTreeItem& parentItem, const std::wstring& path, + DirectoryIterator begin, DirectoryIterator end) +{ + const bool isDirectory = true; + + for (auto itor=begin; itor!=end; ++itor) { + const auto& dir = **itor; + + auto child = std::make_unique<FileTreeItem>( + &parentItem, path, L"", FileTreeItem::Directory, dir.getName(), L""); + + if (dir.isEmpty()) { + child->setLoaded(true); + } else if (showConflicts() || !showArchives()) { + fill(*child, dir, path); + } + + parentItem.add(std::move(child)); + } +} + +void FileTreeModel::fillFiles( + FileTreeItem& parentItem, const std::wstring& path, + const std::vector<FileEntry::Ptr>& files) +{ + const bool isDirectory = false; + + 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<FileTreeItem>( + &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<std::wstring, int> 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<FileTreeItem*>(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) { + return {}; + } + + if (static_cast<std::size_t>(row) >= parent->children().size()) { + return {}; + } + + if (col >= columnCount({})) { + return {}; + } + + auto* item = parent->children()[static_cast<std::size_t>(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 {}; + } + + int row = 0; + for (auto&& child : parent->children()) { + if (child.get() == item) { + return createIndex(row, 0, parent); + } + + ++row; + } + + return {}; +} + +int FileTreeModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) { + return static_cast<int>(m_root.children().size()); + } else { + if (auto* item=itemFromIndex(parent)) { + return static_cast<int>(item->children().size()); + } + } + + return 0; +} + +int FileTreeModel::columnCount(const QModelIndex&) const +{ + return 2; +} + +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: <br>").toStdWString(); + for (std::vector<std::pair<int, std::pair<std::wstring, int>>>::iterator altIter = alternatives.begin(); + altIter != alternatives.end(); ++altIter) { + if (altIter != alternatives.begin()) { + altString << " , "; + } + altString << "<span style=\"white-space: nowrap;\"><i>" << m_core.directoryStructure()->getOriginByID(altIter->first).getName() << "</font></span>"; + } + 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 {}; +} diff --git a/src/filetree.h b/src/filetree.h new file mode 100644 index 00000000..a9eac64e --- /dev/null +++ b/src/filetree.h @@ -0,0 +1,110 @@ +#include "directoryentry.h" +#include <QAbstractItemModel> +#include <QFileIconProvider> + +class OrganizerCore; + +class FileTreeItem +{ +public: + enum Flag + { + NoFlags = 0x00, + Directory = 0x01, + FromArchive = 0x02, + Conflicted = 0x04 + }; + + Q_DECLARE_FLAGS(Flags, Flag); + + FileTreeItem(); + FileTreeItem( + FileTreeItem* parent, + std::wstring virtualParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod); + + FileTreeItem(const FileTreeItem&) = delete; + FileTreeItem& operator=(const FileTreeItem&) = delete; + FileTreeItem(FileTreeItem&&) = default; + FileTreeItem& operator=(FileTreeItem&&) = default; + + void add(std::unique_ptr<FileTreeItem> child); + const std::vector<std::unique_ptr<FileTreeItem>>& children() const; + + FileTreeItem* parent(); + const QString& filename() const; + const QString& mod() const; + bool isFromArchive() const; + bool isHidden() const; + bool isConflicted() const;; + QFileIconProvider::IconType icon() const; + + void setLoaded(bool b); + +private: + FileTreeItem* m_parent; + QString m_virtualParentPath; + QString m_realPath; + Flags m_flags; + QString m_file; + QString m_mod; + bool m_loaded; + std::vector<std::unique_ptr<FileTreeItem>> m_children; +}; + + +class FileTreeModel : public QAbstractItemModel +{ + Q_OBJECT; + +public: + enum Flag + { + NoFlags = 0x00, + Conflicts = 0x01, + Archives = 0x02 + }; + + Q_DECLARE_FLAGS(Flags, Flag); + + FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); + + void setFlags(Flags f); + void refresh(); + + QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent={}) const override; + int columnCount(const QModelIndex& parent={}) const override; + QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; + QVariant headerData(int i, Qt::Orientation ori, int role=Qt::DisplayRole) const override; + +private: + using DirectoryIterator = std::vector<MOShared::DirectoryEntry*>::const_iterator; + OrganizerCore& m_core; + mutable FileTreeItem m_root; + Flags m_flags; + QIcon m_fileIcon, m_directoryIcon; + + bool showConflicts() const; + bool showArchives() const; + + void fill( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath); + + void fillDirectories( + FileTreeItem& parentItem, const std::wstring& path, + DirectoryIterator begin, DirectoryIterator end); + + void fillFiles( + FileTreeItem& parentItem, const std::wstring& path, + const std::vector<MOShared::FileEntry::Ptr>& files); + + std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; + + FileTreeItem* itemFromIndex(const QModelIndex& index) const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); +Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeItem::Flags); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d0daafc0..b27122ef 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1075,7 +1075,7 @@ p, li { white-space: pre-wrap; } <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QTreeWidget" name="dataTree"> + <widget class="QTreeView" name="dataTree"> <property name="contextMenuPolicy"> <enum>Qt::CustomContextMenu</enum> </property> @@ -1088,19 +1088,9 @@ p, li { white-space: pre-wrap; } <property name="uniformRowHeights"> <bool>true</bool> </property> - <attribute name="headerDefaultSectionSize"> + <attribute name="headerMinimumSectionSize"> <number>400</number> </attribute> - <column> - <property name="text"> - <string>File</string> - </property> - </column> - <column> - <property name="text"> - <string>Mod</string> - </property> - </column> </widget> </item> </layout> |
