Qt: Conversation/Endpoint table Filter by different QVariant types

Return the percentage in the UNFORMATTED_DISPLAYDATA converted
from our string so it it compares properly with an equality filter.

Use QVariant comparisons where appropriate so that Filter by
works as expected with types that are not integers, such as doubles
the City/Country strings from GeoIP, or when absolute start time
is used for conversation starts.

Fix #18738.
This commit is contained in:
John Thacker 2023-01-13 23:44:48 -05:00
parent a201eaf118
commit 4d3d856d8f
2 changed files with 108 additions and 10 deletions

View File

@ -447,9 +447,14 @@ QVariant EndpointDataModel::data(const QModelIndex &idx, int role) const
qlonglong totalPackets = (qlonglong)(item->tx_frames_total + item->rx_frames_total);
qlonglong packets = (qlonglong)(item->tx_frames + item->rx_frames);
percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets;
return QString::number(percent, 'f', 2) + "%";
}
return role == Qt::DisplayRole ? QString::number(percent, 'f', 2) + "%" : (QVariant)percent;
QString rounded = QString::number(percent, 'f', 2);
/* Qt guarantees that this roundtrip conversion compares equally,
* so filtering with equality will work as expected.
* Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split
* into one used for raw data export, and one used for comparisons.
*/
return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble());
}
case ENDP_COLUMN_PKT_AB:
return role == Qt::DisplayRole ? QString("%L1").arg((qlonglong)item->tx_frames) : QVariant((qlonglong) item->tx_frames);
@ -704,7 +709,14 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
qlonglong packets = (qlonglong)(conv_item->tx_frames + conv_item->rx_frames);
percent = totalPackets == 0 ? 0 : (double) packets * 100 / (double) totalPackets;
}
return role == Qt::DisplayRole ? QString::number(percent, 'f', 2) + "%" : (QVariant)percent;
QString rounded = QString::number(percent, 'f', 2);
/* Qt guarantees that this roundtrip conversion compares equally,
* so filtering with equality will work as expected.
* XXX: Perhaps the UNFORMATTED_DISPLAYDATA role shoud be split
* into one used for raw data export and comparisions with each
* other, and another for comparing with filters?
*/
return role == Qt::DisplayRole ? rounded + "%" : QVariant(rounded.toDouble());
}
case CONV_COLUMN_PKT_AB:
{
@ -727,7 +739,11 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
if (_absoluteTime) {
nstime_t *abs_time = &conv_item->start_abs_time;
QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(abs_time));
return role == Qt::DisplayRole ? abs_dt.toString("hh:mm:ss.zzzz") : (QVariant)abs_dt;
/* XXX: Should the display include the date as well? More
* clutter, but captures can span midnight. It's probably
* fine so long as the capture isn't more than 24 hours.
*/
return role == Qt::DisplayRole ? abs_dt.time().toString(Qt::ISODateWithMs) : QVariant(abs_dt);
} else {
return role == Qt::DisplayRole ?
QString::number(nstime_to_sec(&conv_item->start_time), 'f', width) :

View File

@ -329,13 +329,95 @@ bool TrafficDataFilterProxy::filterAcceptsRow(int source_row, const QModelIndex
QVariant data = srcIdx.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
bool filtered = false;
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = data.toLongLong() < _filterText.toLongLong();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = data.toLongLong() > _filterText.toLongLong();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL) {
filtered = data.toLongLong() == _filterText.toLongLong();
/* QVariant comparisons coerce to the first parameter type, so
* putting data first and converting the string to it is important.
*/
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
/* QVariant::compare coerces strings to numeric types, but does
* not try to automatically convert them to datetime related types.
*/
QVariant rhs = QVariant(_filterText);
if (data.userType() == QMetaType::QDateTime) {
/* When we display start time in absolute format, we only
* display the time portion, so users will expect to enter
* time-only filters. Convert to QTime instead of QDateTime.
* (Sorting in the table will use the date as well, but it's
* unreasonable to expect users to type in a date that they
* can't see when filtering.)
*/
QTime filterTime = QTime::fromString(_filterText, Qt::ISODateWithMs);
if (filterTime.isValid()) {
rhs.setValue(filterTime);
} else {
rhs = QVariant();
}
data.setValue(data.toTime());
}
QPartialOrdering result = QVariant::compare(data, rhs);
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = result < 0;
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = result > 0;
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
filtered = result == 0;
#else
/* The comparisons are deprecated in 5.15. This is most of the
* implementation of QAbstractItemModelPrivate::isVariantLessThan
* from the Qt source.
*/
if (_filterText.isEmpty())
filtered = true;
else if (data.isNull())
filtered = false;
else {
switch (data.userType()) {
case QMetaType::Int:
case QMetaType::UInt:
case QMetaType::LongLong:
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = data.toLongLong() < _filterText.toLongLong();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = data.toLongLong() > _filterText.toLongLong();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
filtered = data.toLongLong() == _filterText.toLongLong();
break;
case QMetaType::Float:
case QMetaType::Double:
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = data.toDouble() < _filterText.toDouble();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = data.toDouble() > _filterText.toDouble();
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
filtered = data.toDouble() == _filterText.toDouble();
break;
case QMetaType::QDateTime:
case QMetaType::QTime:
/* When we display start time in absolute format, we only
* display the time portion, so users will expect to enter
* time-only filters. Convert to QTime instead of QDateTime.
*/
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = data.toTime() < QTime::fromString(_filterText, Qt::ISODateWithMs);
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = data.toTime() > QTime::fromString(_filterText, Qt::ISODateWithMs);
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
filtered = data.toTime() == QTime::fromString(_filterText, Qt::ISODateWithMs);
break;
case QMetaType::QString:
default:
/* XXX: We don't do UTF-8 aware coallating in Packet List
* (because it's slow), but possibly could here.
*/
if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_LESS)
filtered = data.toString() < _filterText;
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_GREATER)
filtered = data.toString() > _filterText;
else if (_filterOn == TrafficDataFilterProxy::TRAFFIC_DATA_EQUAL)
filtered = data.toString() == _filterText;
break;
}
}
#endif
if (!filtered)
return false;