This commit is contained in:
Martchus 2024-04-12 18:06:36 +02:00
parent 455c0e343c
commit a42b4d364f
3 changed files with 85 additions and 45 deletions

View File

@ -253,7 +253,8 @@ public Q_SLOTS:
public: public:
// methods to GET or POST information from/to Syncthing (non-slots) // methods to GET or POST information from/to Syncthing (non-slots)
QMetaObject::Connection browse(const QString &dirId, const QString &prefix, int level, std::function<void (std::vector<std::unique_ptr<SyncthingItem> > &&, QString &&)> &&callback); QMetaObject::Connection browse(const QString &dirId, const QString &prefix, int level,
std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback);
Q_SIGNALS: Q_SIGNALS:
void newConfig(const QJsonObject &rawConfig); void newConfig(const QJsonObject &rawConfig);
@ -368,7 +369,7 @@ private Q_SLOTS:
private: private:
// handler to evaluate results from request...() methods // handler to evaluate results from request...() methods
void readBrowse(const QString &dirId, int levels, std::function<void (std::vector<std::unique_ptr<SyncthingItem> > &&, QString &&)> &&callback); void readBrowse(const QString &dirId, int levels, std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback);
// internal helper methods // internal helper methods
struct Reply { struct Reply {

View File

@ -1590,7 +1590,8 @@ void SyncthingConnection::readRevert()
* consume results of a specific request. Errors are still reported via the error() signal so there's no extra error handling * consume results of a specific request. Errors are still reported via the error() signal so there's no extra error handling
* required. Note that \a callback is *not* invoked in the error case. * required. Note that \a callback is *not* invoked in the error case.
*/ */
QMetaObject::Connection SyncthingConnection::browse(const QString &dirId, const QString &prefix, int levels, std::function<void (std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback) QMetaObject::Connection SyncthingConnection::browse(const QString &dirId, const QString &prefix, int levels,
std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback)
{ {
auto query = QUrlQuery(); auto query = QUrlQuery();
query.addQueryItem(QStringLiteral("folder"), formatQueryItem(dirId)); query.addQueryItem(QStringLiteral("folder"), formatQueryItem(dirId));
@ -1621,11 +1622,11 @@ static void readSyncthingItems(const QJsonArray &array, std::vector<std::unique_
item->name = jsonItemObj.value(QLatin1String("name")).toString(); item->name = jsonItemObj.value(QLatin1String("name")).toString();
item->modificationTime = CppUtilities::DateTime::fromIsoStringGmt(jsonItemObj.value(QLatin1String("modTime")).toString().toUtf8().data()); item->modificationTime = CppUtilities::DateTime::fromIsoStringGmt(jsonItemObj.value(QLatin1String("modTime")).toString().toUtf8().data());
item->size = static_cast<std::size_t>(jsonItemObj item->size = static_cast<std::size_t>(jsonItemObj
.value(QLatin1String("size")) .value(QLatin1String("size"))
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
.toInteger() .toInteger()
#else #else
.toDouble() .toDouble()
#endif #endif
); );
item->index = index; item->index = index;
@ -1645,7 +1646,8 @@ static void readSyncthingItems(const QJsonArray &array, std::vector<std::unique_
* \brief Reads the response of browse() and reports results via the specified \a callback. Emits error() in case of an error. * \brief Reads the response of browse() and reports results via the specified \a callback. Emits error() in case of an error.
* \remarks The \a callback is also emitted in the error case (with the error message as second parameter and an empty list of items). * \remarks The \a callback is also emitted in the error case (with the error message as second parameter and an empty list of items).
*/ */
void SyncthingConnection::readBrowse(const QString &dirId, int levels, std::function<void (std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback) void SyncthingConnection::readBrowse(
const QString &dirId, int levels, std::function<void(std::vector<std::unique_ptr<SyncthingItem>> &&, QString &&)> &&callback)
{ {
auto const [reply, response] = prepareReply(); auto const [reply, response] = prepareReply();
if (!reply) { if (!reply) {

View File

@ -27,6 +27,9 @@ SyncthingFileModel::SyncthingFileModel(SyncthingConnection &connection, const Sy
m_root->type = SyncthingItemType::Directory; m_root->type = SyncthingItemType::Directory;
m_connection.browse(m_dirId, QString(), 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) { m_connection.browse(m_dirId, QString(), 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) {
Q_UNUSED(errorMessage) Q_UNUSED(errorMessage)
if (items.empty()) {
return;
}
const auto last = items.size() - 1; const auto last = items.size() - 1;
beginInsertRows(index(0, 0), 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max()); beginInsertRows(index(0, 0), 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
m_root->children = std::move(items); m_root->children = std::move(items);
@ -55,7 +58,7 @@ QHash<int, QByteArray> SyncthingFileModel::roleNames() const
QModelIndex SyncthingFileModel::index(int row, int column, const QModelIndex &parent) const QModelIndex SyncthingFileModel::index(int row, int column, const QModelIndex &parent) const
{ {
if (row < 0 || column < 0 || column > 2) { if (row < 0 || column < 0 || column > 2 || parent.column() > 0) {
return QModelIndex(); return QModelIndex();
} }
if (!parent.isValid()) { if (!parent.isValid()) {
@ -77,19 +80,21 @@ QModelIndex SyncthingFileModel::index(int row, int column, const QModelIndex &pa
QModelIndex SyncthingFileModel::index(const QString &path) const QModelIndex SyncthingFileModel::index(const QString &path) const
{ {
auto parts = path.split(QChar('/'), Qt::SkipEmptyParts); auto parts = path.split(QChar('/'), Qt::SkipEmptyParts);
auto res = index(0, 0);
auto *parent = m_root.get(); auto *parent = m_root.get();
auto res = createIndex(0, 0, parent);
for (const auto &part : parts) { for (const auto &part : parts) {
auto foundPart = false; auto index = 0;
for (const auto &child : parent->children) { for (const auto &child : parent->children) {
if (child->name == part) { if (child->name == part) {
child->parent = parent;
parent = child.get(); parent = child.get();
res = index(static_cast<int>(child->index), 0, res); res = createIndex(index, 0, parent);
foundPart = true; index = -1;
break; break;
} }
++index;
} }
if (!foundPart) { if (index >= 0) {
res = QModelIndex(); res = QModelIndex();
return res; return res;
} }
@ -258,6 +263,14 @@ static void addLevel(std::vector<std::unique_ptr<SyncthingItem>> &items, int lev
addLevel(item->children, level); addLevel(item->children, level);
} }
} }
static void considerFetched(std::vector<std::unique_ptr<SyncthingItem>> &items)
{
for (auto &item : items) {
item->childrenPopulated = true;
considerFetched(item->children);
}
}
/// \endcond /// \endcond
void SyncthingFileModel::fetchMore(const QModelIndex &parent) void SyncthingFileModel::fetchMore(const QModelIndex &parent)
@ -297,41 +310,65 @@ void SyncthingFileModel::processFetchQueue()
return; return;
} }
const auto &path = m_fetchQueue.front(); const auto &path = m_fetchQueue.front();
m_pendingRequest = m_connection.browse(m_dirId, path, 1, [this, p = path](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) { m_pendingRequest = m_connection.browse(
Q_UNUSED(errorMessage) m_dirId, path, 1, [this, p = path](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) mutable {
m_fetchQueue.removeAll(p); Q_UNUSED(errorMessage)
const auto refreshedIndex = index(p); {
if (!refreshedIndex.isValid()) { const auto refreshedIndex = index(p);
return; if (!refreshedIndex.isValid()) {
} m_fetchQueue.removeAll(p);
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer()); processFetchQueue();
if (!refreshedItem->children.empty()) { return;
if (refreshedItem == m_root.get()) { }
beginResetModel(); auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
} else { if (!refreshedItem->children.empty()) {
beginRemoveRows(refreshedIndex, 0, static_cast<int>(refreshedItem->children.size() - 1)); if (false && refreshedItem == m_root.get()) {
beginResetModel();
} else {
considerFetched(refreshedItem->children);
std::cout << "begin remove rows at: " << this->path(refreshedIndex).toStdString() << std::endl;
std::cout << " - from 0 to " << static_cast<int>(refreshedItem->children.size() - 1) << std::endl;
for (int row = 0; row < static_cast<int>(refreshedItem->children.size()); ++row) {
std::cout << " - " << row << " - " << index(row, 0, refreshedIndex).data().toString().toStdString() << std::endl;
}
beginRemoveRows(refreshedIndex, 0, static_cast<int>(refreshedItem->children.size() - 1));
}
std::cout << "old row count: " << rowCount(refreshedIndex) << std::endl;
refreshedItem->children.clear();
if (false && refreshedItem == m_root.get()) {
endResetModel();
} else {
endRemoveRows();
}
std::cout << "new row count: " << rowCount(refreshedIndex) << std::endl;
}
} }
refreshedItem->children.clear(); if (!items.empty()) {
if (refreshedItem == m_root.get()) { QTimer::singleShot(400, this, [this, p = std::move(p), items = std::move(items)]() mutable {
endResetModel(); const auto refreshedIndex = index(p);
} else { if (!refreshedIndex.isValid()) {
endRemoveRows(); m_fetchQueue.removeAll(p);
processFetchQueue();
return;
}
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
const auto last = items.size() - 1;
addLevel(items, refreshedItem->level);
for (auto &item : items) {
item->parent = refreshedItem;
}
beginInsertRows(
refreshedIndex, 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
refreshedItem->children = std::move(items);
refreshedItem->childrenPopulated = true;
endInsertRows();
m_fetchQueue.removeAll(p);
processFetchQueue();
});
} }
} });
if (!items.empty()) {
const auto last = items.size() - 1;
addLevel(items, refreshedItem->level);
for (auto &item : items) {
item->parent = refreshedItem;
}
beginInsertRows(refreshedIndex, 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
refreshedItem->children = std::move(items);
refreshedItem->childrenPopulated = true;
endInsertRows();
}
processFetchQueue();
});
} }
} // namespace Data } // namespace Data