syncthingtray/syncthingmodel/syncthingfilemodel.cpp

683 lines
24 KiB
C++
Raw Permalink Normal View History

2024-04-01 18:57:18 +02:00
#include "./syncthingfilemodel.h"
2024-05-13 20:15:10 +02:00
#include "./colors.h"
2024-04-01 18:57:18 +02:00
#include "./syncthingicons.h"
#include <syncthingconnector/syncthingconnection.h>
#include <syncthingconnector/utils.h>
2024-05-04 22:37:31 +02:00
#include <qtutilities/misc/desktoputils.h>
2024-04-01 18:57:18 +02:00
#include <c++utilities/conversion/stringconversion.h>
#include <QAction>
2024-05-04 22:37:31 +02:00
#include <QClipboard>
#include <QGuiApplication>
#include <QNetworkReply>
2024-04-01 18:57:18 +02:00
#include <QStringBuilder>
2024-05-18 13:09:06 +02:00
#include <QtConcurrent>
2024-04-01 18:57:18 +02:00
using namespace std;
using namespace CppUtilities;
namespace Data {
2024-05-04 22:37:31 +02:00
/// \cond
static void populatePath(const QString &root, std::vector<std::unique_ptr<SyncthingItem>> &items)
{
if (root.isEmpty()) {
for (auto &item : items) {
populatePath(item->path = item->name, item->children);
}
} else {
for (auto &item : items) {
populatePath(item->path = root % QChar('/') % item->name, item->children);
}
}
}
static void addErrorItem(std::vector<std::unique_ptr<SyncthingItem>> &items, QString &&errorMessage)
{
if (errorMessage.isEmpty()) {
return;
}
auto &errorItem = items.emplace_back(std::make_unique<SyncthingItem>());
errorItem->name = std::move(errorMessage);
errorItem->type = SyncthingItemType::Error;
errorItem->childrenPopulated = true;
}
static void addLoadingItem(std::vector<std::unique_ptr<SyncthingItem>> &items)
{
if (!items.empty()) {
return;
}
auto &loadingItem = items.emplace_back(std::make_unique<SyncthingItem>());
loadingItem->name = QStringLiteral("Loading…");
loadingItem->type = SyncthingItemType::Loading;
loadingItem->childrenPopulated = true;
}
2024-05-04 22:37:31 +02:00
/// \endcond
2024-04-07 23:29:23 +02:00
SyncthingFileModel::SyncthingFileModel(SyncthingConnection &connection, const SyncthingDir &dir, QObject *parent)
2024-04-01 18:57:18 +02:00
: SyncthingModel(connection, parent)
, m_connection(connection)
2024-04-07 23:29:23 +02:00
, m_dirId(dir.id)
, m_root(std::make_unique<SyncthingItem>())
2024-04-01 18:57:18 +02:00
{
2024-05-04 22:37:31 +02:00
if (m_connection.isLocal()) {
m_localPath = dir.pathWithoutTrailingSlash().toString();
2024-05-13 20:15:10 +02:00
connect(&m_localItemLookup, &QFutureWatcherBase::finished, this, &SyncthingFileModel::handleLocalLookupFinished);
2024-05-04 22:37:31 +02:00
}
2024-04-07 23:29:23 +02:00
m_root->name = dir.displayName();
m_root->modificationTime = dir.lastFileTime;
m_root->size = dir.globalStats.bytes;
m_root->type = SyncthingItemType::Directory;
m_root->path = QStringLiteral(""); // assign an empty QString that is not null
2024-05-13 20:15:10 +02:00
m_fetchQueue.append(m_root->path);
processFetchQueue();
2024-04-01 18:57:18 +02:00
}
SyncthingFileModel::~SyncthingFileModel()
{
QObject::disconnect(m_pendingRequest.connection);
delete m_pendingRequest.reply;
2024-04-01 18:57:18 +02:00
}
QHash<int, QByteArray> SyncthingFileModel::roleNames() const
{
const static auto roles = QHash<int, QByteArray>{
{ NameRole, "name" },
{ SizeRole, "size" },
{ ModificationTimeRole, "modificationTime" },
2024-05-04 22:37:31 +02:00
{ PathRole, "path" },
2024-04-01 18:57:18 +02:00
{ Actions, "actions" },
{ ActionNames, "actionNames" },
{ ActionIcons, "actionIcons" },
};
return roles;
}
QModelIndex SyncthingFileModel::index(int row, int column, const QModelIndex &parent) const
{
2024-04-07 23:29:23 +02:00
if (row < 0 || column < 0 || column > 2 || parent.column() > 0) {
2024-04-01 18:57:18 +02:00
return QModelIndex();
}
if (!parent.isValid()) {
2024-04-07 23:29:23 +02:00
return static_cast<std::size_t>(row) ? QModelIndex() : createIndex(row, column, m_root.get());
2024-04-01 18:57:18 +02:00
}
auto *const parentItem = reinterpret_cast<SyncthingItem *>(parent.internalPointer());
2024-04-07 23:29:23 +02:00
if (!parentItem) {
return QModelIndex();
}
2024-04-01 18:57:18 +02:00
auto &items = parentItem->children;
if (static_cast<std::size_t>(row) >= items.size()) {
return QModelIndex();
}
auto &item = items[static_cast<std::size_t>(row)];
2024-04-07 23:29:23 +02:00
item->parent = parentItem;
return createIndex(row, column, item.get());
}
QModelIndex SyncthingFileModel::index(const QString &path) const
{
auto parts = path.split(QChar('/'), Qt::SkipEmptyParts);
auto *parent = m_root.get();
auto res = createIndex(0, 0, parent);
for (const auto &part : parts) {
auto index = 0;
for (const auto &child : parent->children) {
if (child->name == part) {
child->parent = parent;
parent = child.get();
res = createIndex(index, 0, parent);
index = -1;
break;
}
++index;
}
if (index >= 0) {
res = QModelIndex();
return res;
}
}
return res;
2024-04-01 18:57:18 +02:00
}
QString SyncthingFileModel::path(const QModelIndex &index) const
{
if (!index.isValid()) {
return QString();
2024-04-01 18:57:18 +02:00
}
auto *item = reinterpret_cast<SyncthingItem *>(index.internalPointer());
return item->isFilesystemItem() ? item->path : QString();
2024-04-01 18:57:18 +02:00
}
QModelIndex SyncthingFileModel::parent(const QModelIndex &child) const
{
if (!child.isValid()) {
return QModelIndex();
}
auto *const childItem = reinterpret_cast<SyncthingItem *>(child.internalPointer());
2024-04-07 23:29:23 +02:00
if (!childItem) {
2024-04-01 18:57:18 +02:00
return QModelIndex();
}
2024-04-07 23:29:23 +02:00
return !childItem->parent ? QModelIndex() : createIndex(static_cast<int>(childItem->parent->index), 0, childItem->parent);
2024-04-01 18:57:18 +02:00
}
QVariant SyncthingFileModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (orientation) {
case Qt::Horizontal:
switch (role) {
case Qt::DisplayRole:
switch (section) {
case 0:
return tr("Name");
case 1:
return tr("Size");
case 2:
return tr("Last modified");
}
break;
default:;
}
break;
default:;
}
return QVariant();
}
Qt::ItemFlags SyncthingFileModel::flags(const QModelIndex &index) const
{
auto f = QAbstractItemModel::flags(index);
if (index.isValid()) {
const auto *const item = reinterpret_cast<SyncthingItem *>(index.internalPointer());
switch (item->type) {
case SyncthingItemType::File:
case SyncthingItemType::Symlink:
case SyncthingItemType::Error:
case SyncthingItemType::Loading:
f |= Qt::ItemNeverHasChildren;
break;
default:;
}
}
if (m_selectionMode) {
f |= Qt::ItemIsUserCheckable;
}
return f;
}
2024-04-01 18:57:18 +02:00
QVariant SyncthingFileModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
auto *const item = reinterpret_cast<SyncthingItem *>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case 0:
return item->name;
case 1:
2024-05-04 22:37:31 +02:00
switch (item->type) {
case SyncthingItemType::File:
return QString::fromStdString(CppUtilities::dataSizeToString(item->size));
case SyncthingItemType::Directory:
return item->childrenPopulated ? tr("%1 elements").arg(item->children.size()) : QString();
default:
return QString();
}
2024-04-01 18:57:18 +02:00
case 2:
2024-05-04 22:37:31 +02:00
switch (item->type) {
case SyncthingItemType::File:
case SyncthingItemType::Directory:
return QString::fromStdString(item->modificationTime.toString());
default:
return QString();
}
2024-04-01 18:57:18 +02:00
}
break;
case Qt::CheckStateRole:
if (!m_selectionMode) {
return QVariant();
}
switch (index.column()) {
case 0:
return QVariant(item->checked);
}
break;
2024-04-01 18:57:18 +02:00
case Qt::DecorationRole: {
const auto &icons = commonForkAwesomeIcons();
switch (index.column()) {
case 0:
switch (item->type) {
case SyncthingItemType::File:
return icons.file;
case SyncthingItemType::Directory:
return icons.folder;
2024-05-04 22:37:31 +02:00
case SyncthingItemType::Symlink:
return icons.link;
case SyncthingItemType::Error:
return icons.exclamationTriangle;
2024-04-01 18:57:18 +02:00
default:
return icons.cogs;
}
}
break;
}
2024-05-13 20:15:10 +02:00
case Qt::ForegroundRole:
if (!item->existsInDb) {
return Colors::gray(m_brightColors);
}
break;
2024-05-04 22:37:31 +02:00
case Qt::ToolTipRole:
switch (index.column()) {
case 0:
return item->isFilesystemItem() ? item->path : item->name;
2024-05-04 22:37:31 +02:00
case 2:
return agoString(item->modificationTime);
}
break;
2024-04-01 18:57:18 +02:00
case NameRole:
return item->name;
case SizeRole:
return static_cast<qsizetype>(item->size);
case ModificationTimeRole:
return QString::fromStdString(item->modificationTime.toString());
2024-05-04 22:37:31 +02:00
case PathRole:
return item->isFilesystemItem() ? item->path : QString();
2024-05-04 22:37:31 +02:00
case Actions: {
auto res = QStringList();
res.reserve(3);
2024-04-01 18:57:18 +02:00
if (item->type == SyncthingItemType::Directory) {
2024-05-04 22:37:31 +02:00
res << QStringLiteral("refresh");
2024-04-01 18:57:18 +02:00
}
res << QStringLiteral("toggle-selection");
if (!m_localPath.isEmpty() && item->isFilesystemItem()) {
2024-05-04 22:37:31 +02:00
res << QStringLiteral("open") << QStringLiteral("copy-path");
}
return res;
}
case ActionNames: {
auto res = QStringList();
res.reserve(3);
2024-04-01 18:57:18 +02:00
if (item->type == SyncthingItemType::Directory) {
2024-05-04 22:37:31 +02:00
res << tr("Refresh");
2024-04-01 18:57:18 +02:00
}
res << (item->checked ? tr("Deselect") : tr("Select"));
if (!m_localPath.isEmpty() && item->isFilesystemItem()) {
2024-05-04 22:37:31 +02:00
res << (item->type == SyncthingItemType::Directory ? tr("Browse locally") : tr("Open local version")) << tr("Copy local path");
}
return res;
}
case ActionIcons: {
auto res = QVariantList();
res.reserve(3);
2024-04-01 18:57:18 +02:00
if (item->type == SyncthingItemType::Directory) {
2024-05-04 22:37:31 +02:00
res << QIcon::fromTheme(QStringLiteral("view-refresh"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/view-refresh.svg")));
2024-04-01 18:57:18 +02:00
}
res << QIcon::fromTheme(QStringLiteral("edit-select"));
if (!m_localPath.isEmpty() && item->isFilesystemItem()) {
2024-05-04 22:37:31 +02:00
res << QIcon::fromTheme(QStringLiteral("folder"), QIcon(QStringLiteral(":/icons/hicolor/scalable/places/folder-open.svg")));
res << QIcon::fromTheme(QStringLiteral("edit-copy"), QIcon(QStringLiteral(":/icons/hicolor/scalable/places/edit-copy.svg")));
}
return res;
}
2024-04-01 18:57:18 +02:00
}
return QVariant();
}
bool SyncthingFileModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid()) {
return false;
}
switch (role) {
case Qt::CheckStateRole:
setCheckState(index, static_cast<Qt::CheckState>(value.toInt()));
return true;
}
2024-04-01 18:57:18 +02:00
return false;
}
/// \brief Sets the whether the children of the specified \a item are checked.
static void setChildrenChecked(SyncthingItem *item, Qt::CheckState checkState)
{
for (auto &childItem : item->children) {
setChildrenChecked(childItem.get(), childItem->checked = checkState);
}
}
/// \brief Sets the check state of the specified \a index updating child and parent indexes accordingly.
void SyncthingFileModel::setCheckState(const QModelIndex &index, Qt::CheckState checkState)
{
static const auto roles = QVector<int>{ Qt::CheckStateRole };
auto *const item = reinterpret_cast<SyncthingItem *>(index.internalPointer());
auto affectedParentIndex = index;
item->checked = checkState;
// set the checked state of child items as well
if (checkState != Qt::PartiallyChecked) {
setChildrenChecked(item, checkState);
}
// update the checked state of parent items accordingly
for (auto *parentItem = item->parent; parentItem; parentItem = parentItem->parent) {
auto hasUncheckedSiblings = false;
auto hasCheckedSiblings = false;
for (auto &siblingItem : parentItem->children) {
switch (siblingItem->checked) {
case Qt::Unchecked:
hasUncheckedSiblings = true;
break;
case Qt::PartiallyChecked:
hasUncheckedSiblings = hasCheckedSiblings = true;
break;
case Qt::Checked:
hasCheckedSiblings = true;
}
if (hasUncheckedSiblings && hasCheckedSiblings) {
break;
}
}
auto parentChecked = hasUncheckedSiblings && hasCheckedSiblings ? Qt::PartiallyChecked : (hasUncheckedSiblings ? Qt::Unchecked : Qt::Checked);
if (parentItem->checked == parentChecked) {
break;
}
parentItem->checked = parentChecked;
affectedParentIndex = createIndex(static_cast<int>(parentItem->index), 0, parentItem);
}
// emit dataChanged() events
if (m_selectionMode) {
emit dataChanged(affectedParentIndex, index, roles);
invalidateAllIndicies(roles, affectedParentIndex);
}
}
2024-04-01 18:57:18 +02:00
int SyncthingFileModel::rowCount(const QModelIndex &parent) const
{
auto res = std::size_t();
if (!parent.isValid()) {
2024-04-07 23:29:23 +02:00
res = 1;
2024-04-01 18:57:18 +02:00
} else {
auto *const parentItem = reinterpret_cast<SyncthingItem *>(parent.internalPointer());
res = parentItem->childrenPopulated || parentItem->type != SyncthingItemType::Directory ? parentItem->children.size() : 1;
}
return res < std::numeric_limits<int>::max() ? static_cast<int>(res) : std::numeric_limits<int>::max();
}
int SyncthingFileModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 3;
}
bool SyncthingFileModel::canFetchMore(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return false;
}
auto *const parentItem = reinterpret_cast<SyncthingItem *>(parent.internalPointer());
return !parentItem->childrenPopulated && parentItem->type == SyncthingItemType::Directory;
}
void SyncthingFileModel::fetchMore(const QModelIndex &parent)
{
const auto parentPath = path(parent);
if (parentPath.isNull() || m_fetchQueue.contains(parentPath)) {
2024-04-01 18:57:18 +02:00
return;
}
m_fetchQueue.append(parentPath);
2024-04-01 18:57:18 +02:00
if (m_fetchQueue.size() == 1) {
processFetchQueue();
}
}
void SyncthingFileModel::triggerAction(const QString &action, const QModelIndex &index)
{
if (action == QLatin1String("refresh")) {
fetchMore(index);
return;
} else if (action == QLatin1String("toggle-selection")) {
auto *const item = static_cast<SyncthingItem *>(index.internalPointer());
setSelectionModeEnabled(true);
setData(index, item->checked != Qt::Checked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
2024-04-01 18:57:18 +02:00
}
2024-05-04 22:37:31 +02:00
if (m_localPath.isEmpty()) {
return;
}
const auto relPath = index.data(PathRole).toString();
const auto path = relPath.isEmpty() ? m_localPath : QString(m_localPath % QChar('/') % relPath);
if (action == QLatin1String("open")) {
QtUtilities::openLocalFileOrDir(path);
} else if (action == QLatin1String("copy-path")) {
if (auto *const clipboard = QGuiApplication::clipboard()) {
clipboard->setText(path);
}
}
2024-04-01 18:57:18 +02:00
}
QList<QAction *> SyncthingFileModel::selectionActions()
{
auto res = QList<QAction *>();
if (!m_selectionMode) {
return res;
}
auto *const discardAction = new QAction(tr("Discard selection"), this);
discardAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
connect(discardAction, &QAction::triggered, this, [this] {
setSelectionModeEnabled(false);
setData(QModelIndex(), Qt::Unchecked, Qt::CheckStateRole);
});
res << discardAction;
return res;
}
void SyncthingFileModel::setSelectionModeEnabled(bool selectionModeEnabled)
{
if (m_selectionMode != selectionModeEnabled) {
m_selectionMode = selectionModeEnabled;
invalidateAllIndicies(QVector<int>{ Qt::CheckStateRole });
}
}
2024-04-01 18:57:18 +02:00
void SyncthingFileModel::handleConfigInvalidated()
{
}
void SyncthingFileModel::handleNewConfigAvailable()
{
}
void SyncthingFileModel::handleForkAwesomeIconsChanged()
{
invalidateAllIndicies(QVector<int>({ Qt::DecorationRole }));
}
2024-05-13 20:15:10 +02:00
void SyncthingFileModel::handleBrightColorsChanged()
{
invalidateAllIndicies(QVector<int>({ Qt::ForegroundRole }));
}
void SyncthingFileModel::processFetchQueue(const QString &lastItemPath)
2024-04-01 18:57:18 +02:00
{
2024-05-13 20:15:10 +02:00
if (!lastItemPath.isNull()) {
m_fetchQueue.removeAll(lastItemPath);
}
2024-04-01 18:57:18 +02:00
if (m_fetchQueue.isEmpty()) {
2024-05-13 20:15:10 +02:00
emit fetchQueueEmpty();
2024-04-01 18:57:18 +02:00
return;
}
2024-04-07 23:29:23 +02:00
const auto &path = m_fetchQueue.front();
const auto rootIndex = index(path);
if (!rootIndex.isValid()) {
2024-05-13 20:15:10 +02:00
processFetchQueue(path);
return;
}
// add loading item if there are items yet at all
auto *rootItem = reinterpret_cast<SyncthingItem *>(rootIndex.internalPointer());
if (rootItem->children.empty()) {
beginInsertRows(rootIndex, 0, 0);
addLoadingItem(rootItem->children);
endInsertRows();
}
2024-05-13 20:15:10 +02:00
// query directory entries from Syncthing database
if (rootItem->existsInDb) {
2024-05-18 13:09:06 +02:00
m_pendingRequest
= m_connection.browse(m_dirId, path, 1, [this](std::vector<std::unique_ptr<SyncthingItem>> &&items, QString &&errorMessage) mutable {
m_pendingRequest.reply = nullptr;
addErrorItem(items, std::move(errorMessage));
const auto refreshedIndex = index(m_pendingRequest.forPath);
if (!refreshedIndex.isValid()) {
processFetchQueue(m_pendingRequest.forPath);
return;
}
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
const auto previousChildCount = refreshedItem->children.size();
if (previousChildCount) {
beginRemoveRows(refreshedIndex, 0, static_cast<int>(refreshedItem->children.size() - 1));
refreshedItem->children.clear();
endRemoveRows();
}
if (!items.empty()) {
const auto last = items.size() - 1;
for (auto &item : items) {
item->parent = refreshedItem;
}
populatePath(refreshedItem->path, items);
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;
if (refreshedItem->checked == Qt::Checked) {
setChildrenChecked(refreshedItem, Qt::Checked);
}
2024-05-18 13:09:06 +02:00
endInsertRows();
}
if (refreshedItem->children.size() != previousChildCount) {
const auto sizeIndex = refreshedIndex.siblingAtColumn(1);
emit dataChanged(sizeIndex, sizeIndex, QVector<int>{ Qt::DisplayRole });
}
if (!m_pendingRequest.localLookup.isCanceled()) {
m_pendingRequest.refreshedIndex = refreshedIndex;
m_localItemLookup.setFuture(m_pendingRequest.localLookup);
} else {
processFetchQueue(m_pendingRequest.forPath);
}
});
2024-05-13 20:15:10 +02:00
} else {
m_pendingRequest = SyncthingConnection::QueryResult();
}
m_pendingRequest.forPath = path;
// lookup the directory entries locally to also show ignored files
if (m_localPath.isEmpty()) {
return;
}
m_pendingRequest.localLookup = QtConcurrent::run([dir = QDir(m_localPath % QChar('/') % path)] {
auto items = std::make_shared<std::map<QString, SyncthingItem>>();
auto entries = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
for (const auto &entry : entries) {
const auto entryName = entry.fileName();
auto &item = (*items)[entryName];
item.name = entryName;
item.existsInDb = false;
item.existsLocally = true;
item.size = static_cast<std::size_t>(entry.size());
item.modificationTime = DateTime::unixEpochStart() + TimeSpan(TimeSpan::ticksPerMillisecond * entry.lastModified().toMSecsSinceEpoch());
if (entry.isSymbolicLink()) {
item.type = SyncthingItemType::Symlink;
} else if (entry.isDir()) {
item.type = SyncthingItemType::Directory;
} else {
item.type = SyncthingItemType::File;
2024-05-04 22:37:31 +02:00
}
2024-05-13 20:15:10 +02:00
}
return items;
});
if (!rootItem->existsInDb) {
m_pendingRequest.refreshedIndex = rootIndex;
m_localItemLookup.setFuture(m_pendingRequest.localLookup);
}
}
void SyncthingFileModel::handleLocalLookupFinished()
{
// get refreshed index/item
const auto &refreshedIndex = m_pendingRequest.refreshedIndex;
if (!refreshedIndex.isValid()) {
processFetchQueue(m_pendingRequest.forPath);
return;
}
auto *const refreshedItem = reinterpret_cast<SyncthingItem *>(refreshedIndex.internalPointer());
auto &items = refreshedItem->children;
const auto previousChildCount = items.size();
refreshedItem->childrenPopulated = true;
// clear loading item
if (!refreshedItem->existsInDb && !items.empty()) {
const auto last = items.size() - 1;
beginRemoveRows(refreshedIndex, 0, last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max());
items.clear();
endRemoveRows();
}
// get result from local lookup
auto res = m_pendingRequest.localLookup.result();
if (!res || res->empty()) {
processFetchQueue(m_pendingRequest.forPath);
return;
}
// mark items from the database query as locally existing if they do; mark items from local lookup as existing in the db if they do
auto &localItems = *res;
for (auto &child : items) {
2024-05-13 20:15:10 +02:00
auto localItemIter = localItems.find(child->name);
if (localItemIter == localItems.end()) {
continue;
}
child->existsLocally = true;
localItemIter->second.existsInDb = true;
}
// insert items from local lookup that are not already present via the database query (probably ignored files)
for (auto &[localItemName, localItem] : localItems) {
if (localItem.existsInDb) {
continue;
}
const auto last = items.size();
const auto index = last < std::numeric_limits<int>::max() ? static_cast<int>(last) : std::numeric_limits<int>::max();
beginInsertRows(refreshedIndex, index, index);
auto &item = items.emplace_back(std::make_unique<SyncthingItem>(std::move(localItem)));
item->parent = refreshedItem;
item->index = last;
if (refreshedItem->checked == Qt::Checked) {
setChildrenChecked(item.get(), item->checked = Qt::Checked);
}
2024-05-13 20:15:10 +02:00
populatePath(item->path = refreshedItem->path % QChar('/') % item->name, item->children);
endInsertRows();
}
if (refreshedItem->children.size() != previousChildCount) {
const auto sizeIndex = refreshedIndex.sibling(refreshedIndex.row(), 1);
emit dataChanged(sizeIndex, sizeIndex, QVector<int>{ Qt::DisplayRole });
}
processFetchQueue(m_pendingRequest.forPath);
}
SyncthingFileModel::QueryResult &SyncthingFileModel::QueryResult::operator=(SyncthingConnection::QueryResult &&other)
{
reply = other.reply;
connection = std::move(other.connection);
localLookup = QFuture<LocalLookupRes>();
refreshedIndex = QModelIndex();
return *this;
2024-04-01 18:57:18 +02:00
}
} // namespace Data