Positioner: Remove invalid or nonexistant files

Adds a signal so that whenever a file is deleted/moved to trash, we
know to remove it from positioner configuration.

On listing completion, we now get a list of items that actually
exist, so that positioner can remove the extraneous files.

Iterate over all resolutions and skip files that do not exist
in our position array during saving.

BUG: 469445
CCBUG: 507079
FIXED-IN: 6.8.3
This commit is contained in:
Akseli Lahtinen 2026-10-19 14:36:07 +03:00
parent 761476d81b
commit a2a72d5e13
4 changed files with 56 additions and 15 deletions

View file

@ -227,9 +227,9 @@ FolderModel::FolderModel(QObject *parent)
connect(dirLister, &KCoreDirLister::started, this, std::bind(&FolderModel::setStatus, this, Status::Listing));
QObject::connect(dirLister, &KCoreDirLister::completed, this, [this] {
QObject::connect(dirLister, &KCoreDirLister::completed, this, [this, dirLister] {
setStatus(Status::Ready);
Q_EMIT listingCompleted();
Q_EMIT listingCompleted(dirLister->items().urlList());
});
QObject::connect(dirLister, &KCoreDirLister::canceled, this, [this] {
@ -1662,6 +1662,7 @@ void FolderModel::evictFromIsDirCache(const KFileItemList &items)
for (const KFileItem &item : items) {
m_screenMapper->removeFromMap(item.url(), m_currentActivity);
m_isDirCache.remove(item.url());
Q_EMIT itemDeleted(item.url());
}
}

View file

@ -292,7 +292,7 @@ public:
Q_SIGNALS:
void urlChanged() const;
void listingCompleted() const;
void listingCompleted(const QList<QUrl> &items) const;
void listingCanceled() const;
void iconNameChanged() const;
void resolvedUrlChanged() const;
@ -324,6 +324,7 @@ Q_SIGNALS:
void selectionDone();
void creatingNewItemsChanged() const;
void selectionCountChanged() const;
void itemDeleted(const QUrl &url);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;

View file

@ -1061,13 +1061,33 @@ void Positioner::savePositionsConfig()
auto doc = QJsonDocument::fromJson(confdata.toUtf8());
QJsonObject root;
// Iterate over the old data
for (const auto &item : doc.toVariant().toMap().asKeyValueRange()) {
if (item.first != m_resolution) {
root.insert(item.first, QJsonValue::fromVariant(item.second));
// Positions are serialized like this: [stripes, itemsPerStripe, filename, stripe, position]
const auto currentPositions = positions();
for (const auto &[resolution, itemList] : doc.toVariant().toMap().asKeyValueRange()) {
if (resolution != m_resolution) {
QStringList newPositions;
const QStringList positions = itemList.toStringList();
if (positions.size() >= PositionsConstants::headerSize
&& (positions.size() - PositionsConstants::headerSize) % PositionsConstants::bucketSize == 0) {
newPositions.append(positions[0]);
newPositions.append(positions[1]);
for (qsizetype offset = PositionsConstants::headerSize; offset < positions.size(); offset += PositionsConstants::bucketSize) {
const auto name = positions[offset + PositionsConstants::filenameOffset];
const auto stripe = positions[offset + PositionsConstants::stripeOffset];
const auto pos = positions[offset + PositionsConstants::posOffset];
if (currentPositions.contains(name)) {
newPositions.append(name);
newPositions.append(stripe);
newPositions.append(pos);
}
}
}
root.insert(resolution, QJsonValue::fromVariant(newPositions));
}
}
// Append our new item
root.insert(m_resolution, QJsonArray::fromStringList(positions()));
root.insert(m_resolution, QJsonArray::fromStringList(currentPositions));
const QByteArray data = QJsonDocument(root).toJson(QJsonDocument::Compact);
auto config = m_applet->config().group(QStringLiteral("General"));
@ -1125,13 +1145,14 @@ void Positioner::loadChangedPositions()
m_changedPositions.clear();
m_changedPositions.reserve(jsonArr.size());
for (auto it = jsonArr.begin(), itEnd = jsonArr.end(); it != itEnd; ++it) {
const auto filename = it.key();
for (const auto &key : jsonArr.keys()) {
const auto filename = key;
if (filename.isEmpty()) {
continue;
}
const auto values = it.value().toVariant().toStringList();
const auto values = jsonArr[key].toVariant().toStringList();
if (values.size() != 3) {
continue;
}
@ -1287,11 +1308,26 @@ void Positioner::onItemRenamed(const QString &filename, const QString &newFilena
savePositionsConfig();
}
// Save positions only when listing is done, so we
// do not save them during they're being moved due to
// geometry changes
void Positioner::onListingCompleted()
void Positioner::onItemDeleted(const QUrl &url)
{
updatePositionsList();
m_positions.remove(url.toString());
convertFolderModelData();
savePositionsConfig();
}
void Positioner::onListingCompleted(const QList<QUrl> &urls)
{
QHash<QString, GridPosition> newPositions;
for (const auto &url : urls) {
if (m_positions.contains(url.toString())) {
newPositions[url.toString()] = m_positions[url.toString()];
}
}
m_positions = newPositions;
savePositionsConfig();
}
@ -1310,6 +1346,7 @@ void Positioner::connectSignals(FolderModel *model)
connect(m_folderModel, &FolderModel::statusChanged, this, &Positioner::sourceStatusChanged, Qt::UniqueConnection);
connect(m_folderModel, &FolderModel::itemRenamed, this, &Positioner::onItemRenamed, Qt::UniqueConnection);
connect(m_folderModel, &FolderModel::listingCompleted, this, &Positioner::onListingCompleted, Qt::UniqueConnection);
connect(m_folderModel, &FolderModel::itemDeleted, this, &Positioner::onItemDeleted, Qt::UniqueConnection);
}
void Positioner::disconnectSignals(FolderModel *model)
@ -1327,6 +1364,7 @@ void Positioner::disconnectSignals(FolderModel *model)
disconnect(m_folderModel, &FolderModel::statusChanged, this, &Positioner::sourceStatusChanged);
disconnect(m_folderModel, &FolderModel::itemRenamed, this, &Positioner::onItemRenamed);
disconnect(m_folderModel, &FolderModel::listingCompleted, this, &Positioner::onListingCompleted);
disconnect(m_folderModel, &FolderModel::itemDeleted, this, &Positioner::onItemDeleted);
}
#include "moc_positioner.cpp"

View file

@ -142,7 +142,8 @@ private Q_SLOTS:
void sourceRowsRemoved(const QModelIndex &parent, int first, int last);
void sourceLayoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint);
void onItemRenamed(const QString &filename, const QString &newFilename);
void onListingCompleted();
void onItemDeleted(const QUrl &url);
void onListingCompleted(const QList<QUrl> &urls);
private:
void initMaps(int size = -1);