Files
logos-basecamp/src/ModulesFilterProxy.cpp

169 lines
5.7 KiB
C++
Raw Permalink Normal View History

#include "ModulesFilterProxy.h"
#include "ModuleInstanceModel.h"
#include <QAbstractItemModel>
#include <QByteArray>
#include <QHash>
#include <QModelIndex>
#include <QVariant>
ModulesFilterProxy::ModulesFilterProxy(QObject* parent)
: QSortFilterProxyModel(parent)
{
setDynamicSortFilter(true);
// Kept in sync with m_sortRoleName in setSourceModel/setSortRoleName so
// the initial sort works before either setter is called.
sort(0, Qt::AscendingOrder);
// rowCount changes when the underlying model does OR when filters flip —
// fold both into visibleCount so the header's "N of M" readout stays
// live.
connect(this, &QAbstractItemModel::rowsInserted,
this, &ModulesFilterProxy::visibleCountChanged);
connect(this, &QAbstractItemModel::rowsRemoved,
this, &ModulesFilterProxy::visibleCountChanged);
connect(this, &QAbstractItemModel::modelReset,
this, &ModulesFilterProxy::visibleCountChanged);
connect(this, &QAbstractItemModel::layoutChanged,
this, &ModulesFilterProxy::visibleCountChanged);
}
int ModulesFilterProxy::totalCount() const
{
QAbstractItemModel* src = sourceModel();
return src ? src->rowCount() : 0;
}
void ModulesFilterProxy::setSourceModel(QAbstractItemModel* sourceModel)
{
if (QAbstractItemModel* prev = this->sourceModel()) prev->disconnect(this);
QSortFilterProxyModel::setSourceModel(sourceModel);
setSortRole(roleFromName(m_sortRoleName.toUtf8()));
if (sourceModel) {
// Source row-count changes also move totalCount ("M" in "N of M").
connect(sourceModel, &QAbstractItemModel::rowsInserted,
this, &ModulesFilterProxy::totalCountChanged);
connect(sourceModel, &QAbstractItemModel::rowsRemoved,
this, &ModulesFilterProxy::totalCountChanged);
connect(sourceModel, &QAbstractItemModel::modelReset,
this, &ModulesFilterProxy::totalCountChanged);
}
emit totalCountChanged();
emit visibleCountChanged();
}
void ModulesFilterProxy::setSearchText(const QString& s)
{
if (s == m_searchText) return;
m_searchText = s;
invalidateFilter();
emit searchTextChanged();
emit visibleCountChanged();
}
void ModulesFilterProxy::setStateFilter(const QString& s)
{
if (s == m_stateFilter) return;
m_stateFilter = s;
invalidateFilter();
emit stateFilterChanged();
emit visibleCountChanged();
}
void ModulesFilterProxy::setSortRoleName(const QString& name)
{
if (name == m_sortRoleName) return;
m_sortRoleName = name;
setSortRole(roleFromName(name.toUtf8()));
// setSortRole doesn't re-sort by itself.
invalidate();
emit sortRoleNameChanged();
}
2026-07-30 18:30:11 +02:00
void ModulesFilterProxy::applySortOrder(int order)
{
sort(0, static_cast<Qt::SortOrder>(order));
}
int ModulesFilterProxy::roleFromName(const QByteArray& name) const
{
QAbstractItemModel* src = sourceModel();
if (!src) return ModuleInstanceModel::LabelRole;
const auto roles = src->roleNames();
for (auto it = roles.cbegin(); it != roles.cend(); ++it) {
if (it.value() == name) return it.key();
}
return ModuleInstanceModel::LabelRole;
}
bool ModulesFilterProxy::filterAcceptsRow(int sourceRow,
const QModelIndex& sourceParent) const
{
QAbstractItemModel* src = sourceModel();
if (!src) return true;
const QModelIndex idx = src->index(sourceRow, 0, sourceParent);
// State filter.
if (m_stateFilter == QLatin1String("loaded")
&& !src->data(idx, ModuleInstanceModel::IsLoadedRole).toBool()) return false;
if (m_stateFilter == QLatin1String("notLoaded")
&& src->data(idx, ModuleInstanceModel::IsLoadedRole).toBool()) return false;
// Search filter — trimmed + lowercased against a fixed set of textual
// roles. Empty needle accepts everything.
const QString needle = m_searchText.trimmed().toLower();
if (needle.isEmpty()) return true;
static const int textRoles[] = {
ModuleInstanceModel::NameRole,
ModuleInstanceModel::LabelRole,
ModuleInstanceModel::StatusTextRole,
ModuleInstanceModel::DescriptionRole,
ModuleInstanceModel::VersionRole,
};
for (int role : textRoles) {
if (src->data(idx, role).toString().toLower().contains(needle))
return true;
}
return false;
}
bool ModulesFilterProxy::lessThan(const QModelIndex& left,
const QModelIndex& right) const
{
QAbstractItemModel* src = sourceModel();
if (!src) return false;
const int role = sortRole();
const QVariant lv = src->data(left, role);
const QVariant rv = src->data(right, role);
int result = 0;
if (lv.typeId() == QMetaType::Double || rv.typeId() == QMetaType::Double
|| lv.typeId() == QMetaType::Int || rv.typeId() == QMetaType::Int) {
// Numeric roles (cpu, memory) — compare as doubles even if one side
// came in as int.
const double ld = lv.toDouble();
const double rd = rv.toDouble();
if (ld < rd) result = -1;
else if (ld > rd) result = 1;
} else if (lv.typeId() == QMetaType::Bool || rv.typeId() == QMetaType::Bool) {
result = (lv.toBool() ? 1 : 0) - (rv.toBool() ? 1 : 0);
} else {
result = lv.toString().localeAwareCompare(rv.toString());
}
// Stable tie-break by name so equal-status / equal-stat rows don't shuffle
// between refreshes (the core stats poll fires every 2s).
if (result == 0 && role != ModuleInstanceModel::NameRole) {
result = src->data(left, ModuleInstanceModel::NameRole).toString()
.localeAwareCompare(
src->data(right, ModuleInstanceModel::NameRole).toString());
}
return result < 0;
}