6#include <QtCore/QCollator>
7#include <QtCore/QFileInfo>
8#include <QtCore/QLocale>
15 : QAbstractListModel(parent)
23 if (_archivePath == path) {
37 qCWarning(QGCArchiveModelLog) <<
"Unsupported archive URL:" << url;
47 if (_filterMode == mode) {
59 if (parent.isValid()) {
62 return static_cast<int>(_filteredEntries.size());
67 if (!index.isValid() || index.row() < 0 || index.row() >= _filteredEntries.size()) {
91 const int lastSlash = entry.
name.lastIndexOf(QLatin1Char(
'/'));
92 if (lastSlash >= 0 && lastSlash < entry.
name.size() - 1) {
93 return entry.
name.mid(lastSlash + 1);
99 const int lastSlash = entry.
name.lastIndexOf(QLatin1Char(
'/'));
101 return entry.
name.left(lastSlash);
116 static const QHash<int, QByteArray> roles = {
136 if (_filteredEntries.isEmpty() && _allEntries.isEmpty()) {
142 _filteredEntries.clear();
145 const bool hadFiles = _fileCount > 0;
146 const bool hadDirs = _directoryCount > 0;
147 const bool hadSize = _totalSize > 0;
158 _setErrorString(QString());
165 if (index < 0 || index >= _filteredEntries.size()) {
171 result[QStringLiteral(
"name")] = entry.
name;
172 result[QStringLiteral(
"size")] = entry.
size;
173 result[QStringLiteral(
"modified")] = entry.
modified;
174 result[QStringLiteral(
"isDirectory")] = entry.
isDirectory;
175 result[QStringLiteral(
"permissions")] = entry.
permissions;
179 const int lastSlash = entry.
name.lastIndexOf(QLatin1Char(
'/'));
180 if (lastSlash >= 0 && lastSlash < entry.
name.size() - 1) {
181 result[QStringLiteral(
"fileName")] = entry.
name.mid(lastSlash + 1);
182 result[QStringLiteral(
"directory")] = entry.
name.left(lastSlash);
184 result[QStringLiteral(
"fileName")] = entry.
name;
185 result[QStringLiteral(
"directory")] = QString();
193 if (fileName.isEmpty()) {
197 for (
const auto &entry : _allEntries) {
198 if (entry.name == fileName) {
208 return QStringLiteral(
"--");
211 static const char* units[] = {
"B",
"KB",
"MB",
"GB",
"TB" };
212 constexpr int numUnits =
sizeof(units) /
sizeof(units[0]);
214 double size =
static_cast<double>(bytes);
217 while (size >= 1024.0 && unitIndex < numUnits - 1) {
222 if (unitIndex == 0) {
224 return QStringLiteral(
"%1 %2").arg(bytes).arg(QLatin1String(units[unitIndex]));
228 return QStringLiteral(
"%1 %2")
229 .arg(QLocale().toString(size,
'f', 1))
230 .arg(QLatin1String(units[unitIndex]));
233void QGCArchiveModel::_loadArchive()
237 if (_archivePath.isEmpty()) {
243 _setErrorString(QString());
245 qCDebug(QGCArchiveModelLog) <<
"Loading archive:" << _archivePath;
250 if (entries.isEmpty() && !_archivePath.isEmpty()) {
265 for (
const auto &entry : entries) {
266 if (entry.isDirectory) {
275 _allEntries = std::move(entries);
279 const bool dirCountChanged = _directoryCount != dirCount;
280 const bool sizeChanged = _totalSize !=
totalSize;
283 _directoryCount = dirCount;
296 qCDebug(QGCArchiveModelLog) <<
"Loaded" << _allEntries.size() <<
"entries:"
297 << _fileCount <<
"files," << _directoryCount <<
"dirs,"
298 << _totalSize <<
"bytes";
303void QGCArchiveModel::_applyFilter()
307 switch (_filterMode) {
309 _filteredEntries = _allEntries;
313 _filteredEntries.clear();
314 for (
const auto &entry : _allEntries) {
315 if (!entry.isDirectory) {
316 _filteredEntries.append(entry);
322 _filteredEntries.clear();
323 for (
const auto &entry : _allEntries) {
324 if (entry.isDirectory) {
325 _filteredEntries.append(entry);
335void QGCArchiveModel::_setLoading(
bool loading)
345void QGCArchiveModel::_setErrorString(
const QString &
error)
347 if (_errorString ==
error) {
351 _errorString =
error;
#define QGC_LOGGING_CATEGORY(name, categoryStr)
List model for archive contents, suitable for QML ListView binding.
QHash< int, QByteArray > roleNames() const override
static Q_INVOKABLE QString formatSize(qint64 bytes)
Format bytes as human-readable string (e.g., "1.5 MB")
Q_INVOKABLE QVariantMap get(int index) const
Get entry at index (for C++ usage)
void directoryCountChanged()
void archivePathChanged()
void setArchiveUrl(const QUrl &url)
void setArchivePath(const QString &path)
void setFilterMode(FilterMode mode)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
~QGCArchiveModel() override
@ FileNameRole
Just the filename without path (QString)
@ ModifiedRole
Last modified date (QDateTime)
@ IsDirectoryRole
Whether entry is a directory (bool)
@ PermissionsRole
Unix permissions (quint32)
@ FormattedSizeRole
Human-readable size string (QString)
@ DirectoryRole
Parent directory path (QString)
@ SizeRole
Uncompressed size in bytes (qint64)
@ NameRole
Entry name/path (QString)
void errorStringChanged()
Q_INVOKABLE bool contains(const QString &fileName) const
Check if a file exists in the archive.
void loadingComplete(bool success)
Emitted when archive loading completes (success or failure)
Q_INVOKABLE void clear()
Clear the model and reset all properties.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Q_INVOKABLE void refresh()
Reload archive contents (useful after external changes)
FilterMode
Filter modes for showing subsets of entries.
@ FilesOnly
Show only files (no directories)
@ DirectoriesOnly
Show only directories.
@ AllEntries
Show all files and directories.
QString lastErrorString()
Get a human-readable error message from the last operation (thread-local)
QList< ArchiveEntry > listArchiveDetailed(const QString &archivePath, Format format)
bool validateArchive(const QString &archivePath, Format format)
bool isLocalPath(const QString &urlOrPath)
QString toLocalPath(const QString &urlOrPath)
Metadata for a single entry in an archive.
bool isDirectory
True if entry is a directory.
qint64 size
Uncompressed size in bytes.
QString name
Path/name within archive.
quint32 permissions
Unix-style permissions.
QDateTime modified
Last modification time.