summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/datatab.cpp73
-rw-r--r--src/datatab.h3
-rw-r--r--src/filetreeitem.cpp134
-rw-r--r--src/filetreeitem.h59
-rw-r--r--src/filetreemodel.cpp35
-rw-r--r--src/filetreemodel.h1
-rw-r--r--src/mainwindow.cpp2
-rw-r--r--src/shared/directoryentry.cpp21
-rw-r--r--src/shared/directoryentry.h22
9 files changed, 234 insertions, 116 deletions
diff --git a/src/datatab.cpp b/src/datatab.cpp
index af457b33..6b403f66 100644
--- a/src/datatab.cpp
+++ b/src/datatab.cpp
@@ -59,7 +59,7 @@ void DataTab::saveState(Settings& s) const
void DataTab::restoreState(const Settings& s)
{
s.geometry().restoreState(ui.tree->header());
-
+
// prior to 2.3, the list was not sortable, and this remembered in the
// widget state, for whatever reason
ui.tree->setSortingEnabled(true);
@@ -67,7 +67,7 @@ void DataTab::restoreState(const Settings& s)
void DataTab::activated()
{
- //refreshDataTreeKeepExpandedNodes();
+ updateTree();
}
void DataTab::onRefresh()
@@ -79,76 +79,11 @@ void DataTab::onRefresh()
m_core.refreshDirectoryStructure();
}
-void DataTab::refreshDataTree()
+void DataTab::updateTree()
{
m_filetree->refresh();
}
-void DataTab::refreshDataTreeKeepExpandedNodes()
-{
- //m_model->refreshKeepExpandedNodes();
- m_filetree->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);
- while (*it1) {
- QTreeWidgetItem *current = (*it1);
- if (current->isExpanded() && !(current->text(0)=="data")) {
- expandedNodes.append(current->text(0)+"/"+current->parent()->text(0));
- }
- ++it1;
- }
-
- 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);
- QTreeWidgetItemIterator it2(ui.tree, QTreeWidgetItemIterator::HasChildren);
- while (*it2) {
- QTreeWidgetItem *current = (*it2);
- if (!(current->text(0)=="data") && expandedNodes.contains(current->text(0)+"/"+current->parent()->text(0))) {
- current->setExpanded(true);
- }
- ++it2;
- }*/
-}
-
-void DataTab::onItemExpanded(QTreeWidgetItem* item)
-{
- /*if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) {
- // read the data we need from the sub-item, then dispose of it
- QTreeWidgetItem *onDemandDataItem = item->child(0);
- const QString path = onDemandDataItem->data(0, Qt::UserRole + 1).toString();
- std::wstring wspath = path.toStdWString();
- bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool();
-
- std::wstring virtualPath = (wspath + L"\\").substr(6) + item->text(0).toStdWString();
- DirectoryEntry *dir = m_core.directoryStructure()->findSubDirectoryRecursive(virtualPath);
- if (dir != nullptr) {
- QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder);
- QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File);
- updateTo(item, wspath, *dir, conflictsOnly, &fileIcon, &folderIcon);
- } else {
- log::warn("failed to update view of {}", path);
- }
-
- m_removeLater.push_back(item);
-
- QTimer::singleShot(5, [this]{
- for (QTreeWidgetItem *item : m_removeLater) {
- item->removeChild(item->child(0));
- }
- m_removeLater.clear();
- });
- }*/
-}
-
void DataTab::onConflicts()
{
updateOptions();
@@ -172,5 +107,5 @@ void DataTab::updateOptions()
}
m_filetree->model()->setFlags(flags);
- refreshDataTree();
+ updateTree();
}
diff --git a/src/datatab.h b/src/datatab.h
index fd93514c..4545dc5a 100644
--- a/src/datatab.h
+++ b/src/datatab.h
@@ -25,8 +25,7 @@ public:
void restoreState(const Settings& s);
void activated();
- void refreshDataTreeKeepExpandedNodes();
- void refreshDataTree();
+ void updateTree();
signals:
void executablesChanged();
diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp
index a506f0f0..6d42f2dc 100644
--- a/src/filetreeitem.cpp
+++ b/src/filetreeitem.cpp
@@ -4,9 +4,38 @@
#include "util.h"
#include "modinfodialogfwd.h"
#include <log.h>
+#include <utility.h>
using namespace MOBase;
using namespace MOShared;
+namespace fs = std::filesystem;
+
+const QString& directoryFileType()
+{
+ static QString name;
+
+ if (name.isEmpty()) {
+ const DWORD flags = SHGFI_TYPENAME;
+ SHFILEINFOW sfi = {};
+
+ // "." for the current directory, which should always exist
+ const auto r = SHGetFileInfoW(L".", 0, &sfi, sizeof(sfi), flags);
+
+ if (!r) {
+ const auto e = GetLastError();
+
+ log::error(
+ "SHGetFileInfoW failed for folder file type, {}",
+ formatSystemMessage(e));
+
+ name = "File folder";
+ } else {
+ name = QString::fromWCharArray(sfi.szTypeName);
+ }
+ }
+
+ return name;
+}
FileTreeItem::FileTreeItem(
@@ -16,6 +45,7 @@ FileTreeItem::FileTreeItem(
m_parent(parent), m_indexGuess(NoIndexGuess),
m_originID(originID),
m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)),
+ m_wsRealPath(realPath),
m_realPath(QString::fromStdWString(realPath)),
m_flags(flags),
m_wsFile(file),
@@ -94,13 +124,19 @@ public:
return naturalCompare(a->m_mod, b->m_mod);
case FileTreeModel::FileType:
- return naturalCompare(a->meta().type, b->meta().type);
+ return naturalCompare(
+ a->fileType().value_or(QString()),
+ b->fileType().value_or(QString()));
case FileTreeModel::FileSize:
- return threeWayCompare(a->meta().size, b->meta().size);
+ return threeWayCompare(
+ a->fileSize().value_or(0),
+ b->fileSize().value_or(0));
case FileTreeModel::LastModified:
- return threeWayCompare(a->meta().lastModified, b->meta().lastModified);
+ return threeWayCompare(
+ a->lastModified().value_or(QDateTime()),
+ b->lastModified().value_or(QDateTime()));
default:
return 0;
@@ -166,34 +202,88 @@ QFont FileTreeItem::font() const
return f;
}
-const FileTreeItem::Meta& FileTreeItem::meta() const
+std::optional<uint64_t> FileTreeItem::fileSize() const
{
- if (!m_meta) {
- QFileInfo fi(m_realPath);
+ if (m_fileSize.empty()) {
+ std::error_code ec;
+ const auto size = fs::file_size(fs::path(m_wsRealPath), ec);
- SHFILEINFOW sfi = {};
- const auto r = SHGetFileInfoW(
- m_realPath.toStdWString().c_str(), 0, &sfi, sizeof(sfi),
- SHGFI_TYPENAME);
+ if (ec) {
+ log::error("can't get file size for '{}', {}", m_realPath, ec.message());
+ m_fileSize.fail();
+ } else {
+ m_fileSize.set(size);
+ }
+ }
- if (!r) {
- const auto e = GetLastError();
+ return m_fileSize.value;
+}
- log::error(
- "SHGetFileInfoW failed for '{}', {}",
- m_realPath, e);
+std::optional<QDateTime> FileTreeItem::lastModified() const
+{
+ if (m_lastModified.empty()) {
+ if (m_realPath.isEmpty()) {
+ // this is a virtual directory
+ m_lastModified.set({});
+ } else if (isFromArchive()) {
+ // can't get last modified date for files in archives
+ m_lastModified.set({});
+ } else {
+ // looks like a regular file on the filesystem
+ const QFileInfo fi(m_realPath);
+ const auto d = fi.lastModified();
- sfi = {};
+ if (!d.isValid()) {
+ log::error("can't get last modified date for '{}'", m_realPath);
+ m_lastModified.fail();
+ } else {
+ m_lastModified.set(d);
+ }
}
+ }
+
+ return m_lastModified.value;
+}
- m_meta = {
- static_cast<uint64_t>(fi.size()),
- fi.lastModified(),
- QString::fromWCharArray(sfi.szTypeName)
- };
+std::optional<QString> FileTreeItem::fileType() const
+{
+ if (m_fileType.empty()) {
+ getFileType();
}
- return *m_meta;
+ return m_fileType.value;
+}
+
+void FileTreeItem::getFileType() const
+{
+ if (isDirectory()) {
+ m_fileType.set(directoryFileType());
+ return;
+ }
+
+ DWORD flags = SHGFI_TYPENAME;
+
+ if (isFromArchive()) {
+ // files from archives are not on the filesystem; this flag forces
+ // SHGetFileInfoW() to only work with the filename
+ flags |= SHGFI_USEFILEATTRIBUTES;
+ }
+
+ SHFILEINFOW sfi = {};
+ const auto r = SHGetFileInfoW(
+ m_wsRealPath.c_str(), 0, &sfi, sizeof(sfi), flags);
+
+ if (!r) {
+ const auto e = GetLastError();
+
+ log::error(
+ "SHGetFileInfoW failed for '{}', {}",
+ m_realPath, formatSystemMessage(e));
+
+ m_fileType.fail();
+ } else {
+ m_fileType.set(QString::fromWCharArray(sfi.szTypeName));
+ }
}
QFileIconProvider::IconType FileTreeItem::icon() const
diff --git a/src/filetreeitem.h b/src/filetreeitem.h
index fc27b0c0..c5418d43 100644
--- a/src/filetreeitem.h
+++ b/src/filetreeitem.h
@@ -19,13 +19,6 @@ public:
Conflicted = 0x04
};
- struct Meta
- {
- uint64_t size;
- QDateTime lastModified;
- QString type;
- };
-
Q_DECLARE_FLAGS(Flags, Flag);
@@ -136,7 +129,24 @@ public:
QFont font() const;
- const Meta& meta() const;
+ std::optional<uint64_t> fileSize() const;
+ std::optional<QDateTime> lastModified() const;
+ std::optional<QString> fileType() const;
+
+ std::optional<uint64_t> compressedFileSize() const
+ {
+ return m_compressedFileSize;
+ }
+
+ void setFileSize(uint64_t size)
+ {
+ m_fileSize.set(size);
+ }
+
+ void setCompressedFileSize(uint64_t compressedSize)
+ {
+ m_compressedFileSize = compressedSize;
+ }
const QString& realPath() const
{
@@ -210,6 +220,30 @@ public:
QString debugName() const;
private:
+ template <class T>
+ struct Cached
+ {
+ std::optional<T> value;
+ bool failed = false;
+
+ bool empty() const
+ {
+ return !failed && !value;
+ }
+
+ void set(T t)
+ {
+ value = std::move(t);
+ failed = false;
+ }
+
+ void fail()
+ {
+ value = {};
+ failed = true;
+ }
+ };
+
static constexpr std::size_t NoIndexGuess =
std::numeric_limits<std::size_t>::max();
@@ -218,17 +252,24 @@ private:
const int m_originID;
const QString m_virtualParentPath;
+ const std::wstring m_wsRealPath;
const QString m_realPath;
const Flags m_flags;
const std::wstring m_wsFile, m_wsLcFile;
const MOShared::DirectoryEntry::FileKey m_key;
const QString m_file;
const QString m_mod;
- mutable std::optional<Meta> m_meta;
+
+ mutable Cached<uint64_t> m_fileSize;
+ mutable Cached<QDateTime> m_lastModified;
+ mutable Cached<QString> m_fileType;
+ mutable std::optional<uint64_t> m_compressedFileSize;
bool m_loaded;
bool m_expanded;
Children m_children;
+
+ void getFileType() const;
};
#endif // MODORGANIZER_FILETREEITEM_INCLUDED
diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp
index aa053eeb..cb7e94fa 100644
--- a/src/filetreemodel.cpp
+++ b/src/filetreemodel.cpp
@@ -127,7 +127,7 @@ private:
FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
QAbstractItemModel(parent), m_core(core),
m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"<root>"),
- m_flags(NoFlags), m_isRefreshing(false)
+ m_flags(NoFlags)
{
m_root.setExpanded(true);
@@ -136,9 +136,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
void FileTreeModel::refresh()
{
- m_isRefreshing = true;
- Guard g([&]{ m_isRefreshing = false; });
-
TimeThis tt("FileTreeModel::refresh()");
update(m_root, *m_core.directoryStructure(), L"");
}
@@ -695,6 +692,14 @@ std::unique_ptr<FileTreeItem> FileTreeModel::createFileItem(
&parentItem, originID, parentPath, file.getFullPath(), flags,
file.getName(), makeModName(file, originID));
+ if (file.getFileSize() != FileEntry::NoFileSize) {
+ item->setFileSize(file.getFileSize());
+ }
+
+ if (file.getCompressedFileSize() != FileEntry::NoFileSize) {
+ item->setCompressedFileSize(file.getCompressedFileSize());
+ }
+
item->setLoaded(true);
return item;
@@ -716,7 +721,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const
case FileType:
{
- return item->meta().type;
+ return item->fileType().value_or(QString());
}
case FileSize:
@@ -724,13 +729,29 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const
if (item->isDirectory()) {
return {};
} else {
- return localizedByteSize(item->meta().size);
+ QString fs;
+
+ if (auto n=item->fileSize()) {
+ fs = localizedByteSize(*n);
+ }
+
+ if (auto n=item->compressedFileSize()) {
+ return QString("%1 (%2)").arg(fs).arg(localizedByteSize(*n));
+ } else {
+ return fs;
+ }
}
}
case LastModified:
{
- return item->meta().lastModified.toString(Qt::SystemLocaleDate);
+ if (auto d=item->lastModified()) {
+ if (d->isValid()) {
+ return d->toString(Qt::SystemLocaleDate);
+ }
+ }
+
+ return {};
}
default:
diff --git a/src/filetreemodel.h b/src/filetreemodel.h
index 38f611f9..0fbcf521 100644
--- a/src/filetreemodel.h
+++ b/src/filetreemodel.h
@@ -81,7 +81,6 @@ private:
mutable IconFetcher m_iconFetcher;
mutable std::vector<QModelIndex> m_iconPending;
mutable QTimer m_iconPendingTimer;
- bool m_isRefreshing;
Sort m_sort;
bool showConflicts() const
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index c71a877d..bf0dc61f 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2364,7 +2364,7 @@ void MainWindow::directory_refreshed()
//Some better check for the current tab is needed.
if (ui->tabWidget->currentIndex() == 2) {
- m_DataTab->refreshDataTreeKeepExpandedNodes();
+ m_DataTab->updateTree();
}
}
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 4181098c..92b6c2bf 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -167,13 +167,15 @@ private:
FileEntry::FileEntry() :
m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr),
+ m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
m_LastAccessed(time(nullptr))
{
}
FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) :
- m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1),
- m_Parent(parent), m_LastAccessed(time(nullptr))
+ m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
+ m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
+ m_LastAccessed(time(nullptr))
{
}
@@ -1020,7 +1022,7 @@ void DirectoryEntry::removeFiles(const std::set<FileEntry::Index> &indices)
removeFilesFromList(indices);
}
-void DirectoryEntry::insert(
+FileEntry::Ptr DirectoryEntry::insert(
const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
const std::wstring &archive, int order)
{
@@ -1036,6 +1038,8 @@ void DirectoryEntry::insert(
file->addOrigin(origin.getID(), fileTime, archive, order);
origin.addFile(file->getIndex());
+
+ return file;
}
void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
@@ -1085,7 +1089,16 @@ void DirectoryEntry::addFiles(
// add files
for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) {
BSA::File::Ptr file = archiveFolder->getFile(fileIdx);
- insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order);
+
+ auto f = insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order);
+
+ if (f) {
+ if (file->getUncompressedFileSize() > 0) {
+ f->setFileSize(file->getFileSize(), file->getUncompressedFileSize());
+ } else {
+ f->setFileSize(file->getFileSize(), FileEntry::NoFileSize);
+ }
+ }
}
// recurse into subdirectories
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 6102c88f..7e45493d 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -61,6 +61,9 @@ class FileRegister;
class FileEntry
{
public:
+ static constexpr uint64_t NoFileSize =
+ std::numeric_limits<uint64_t>::max();
+
typedef unsigned int Index;
typedef boost::shared_ptr<FileEntry> Ptr;
@@ -150,6 +153,22 @@ public:
return m_FileTime;
}
+ void setFileSize(uint64_t size, uint64_t compressedSize)
+ {
+ m_FileSize = size;
+ m_CompressedFileSize = compressedSize;
+ }
+
+ uint64_t getFileSize() const
+ {
+ return m_FileSize;
+ }
+
+ uint64_t getCompressedFileSize() const
+ {
+ return m_CompressedFileSize;
+ }
+
private:
Index m_Index;
std::wstring m_Name;
@@ -158,6 +177,7 @@ private:
AlternativesVector m_Alternatives;
DirectoryEntry *m_Parent;
mutable FILETIME m_FileTime;
+ uint64_t m_FileSize, m_CompressedFileSize;
time_t m_LastAccessed;
@@ -480,7 +500,7 @@ private:
DirectoryEntry(const DirectoryEntry &reference);
- void insert(
+ FileEntry::Ptr insert(
const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
const std::wstring &archive, int order);