Qt: Fix conversation dialog timeline graph columns

Remove the separate copy of the conversation column type
enum in ConversationDialog. It doesn't correspond to the
enum in ATapDataModel, because it doesn't include the
Conversation ID and Total columns.

Delegates set to the Traffic Tree (a QTreeView) apply to
the displayed column number, not the what is displayed
in the column. That is, when columns are hidden (filtered),
the delegates stay with the old column number and end up
being displayed on new content. That's not what we want;
we want to always have the timeline graph on the Start and
Duration columns. Moving around the Delegate depending on
which columns are filtered is complicated; just set the
TimeLine delegate to the entire TrafficTree, and use the
default paint if we're on any column other than Start or
Duration. This does mean that we don't need to store a
map of Delegates.

Use a slightly different way to get TimelineDelegate to
paint the rect with no text using the current style, so
we can use the default paint with text for other columns.

The timeline graph needs to get the value of the Start and
Duration columns as filtered by the proxy column, so have
the model pass in indices so that they can be mapped to
the current displayed column.

Have the timeline graph apply to just the Start or just the
Duration column if only one is visible.

Together these compute the proper pixel values to apply
the timeline graph to the Start and Duration columns regardless
of which columns are displayed and which are filtered, in any
tab, regardless of what optional columns appear in that tab.

Fix #18860
This commit is contained in:
John Thacker 2023-02-16 07:31:31 -05:00
parent a2b584b8bd
commit 33493cb602
6 changed files with 50 additions and 58 deletions

View File

@ -50,25 +50,6 @@
// - The value of 'Rel start' and 'Duration' in "Conversations" no need too precise https://gitlab.com/wireshark/wireshark/-/issues/12803
typedef enum {
CONV_COLUMN_SRC_ADDR,
CONV_COLUMN_SRC_PORT,
CONV_COLUMN_DST_ADDR,
CONV_COLUMN_DST_PORT,
CONV_COLUMN_PACKETS,
CONV_COLUMN_BYTES,
CONV_COLUMN_PKT_AB,
CONV_COLUMN_BYTES_AB,
CONV_COLUMN_PKT_BA,
CONV_COLUMN_BYTES_BA,
CONV_COLUMN_START,
CONV_COLUMN_DURATION,
CONV_COLUMN_BPS_AB,
CONV_COLUMN_BPS_BA,
CONV_NUM_COLUMNS,
CONV_INDEX_COLUMN = CONV_NUM_COLUMNS
} conversation_column_type_e;
static const QString table_name_ = QObject::tr("Conversation");
static ATapDataModel * createModel(int protoId, QString filter)
@ -91,8 +72,8 @@ ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf) :
trafficList()->setProtocolInfo(table_name_, &(recent.conversation_tabs));
trafficTab()->setProtocolInfo(table_name_, trafficList(), &(recent.conversation_tabs_columns), &createModel);
trafficTab()->setDelegate(CONV_COLUMN_START, &createDelegate);
trafficTab()->setDelegate(CONV_COLUMN_DURATION, &createDelegate);
trafficTab()->setDelegate(&createDelegate);
trafficTab()->setDelegate(&createDelegate);
trafficTab()->setFilter(cf.displayFilter());
connect(trafficTab(), &TrafficTab::filterAction, this, &ConversationDialog::filterAction);

View File

@ -15,6 +15,7 @@
#include <QApplication>
#include <QPainter>
#include <QTreeView>
#include <QAbstractProxyModel>
// XXX We might want to move this to conversation_dialog.cpp.
@ -40,26 +41,52 @@ void TimelineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
QStyleOptionViewItem option_vi = option;
QStyledItemDelegate::initStyleOption(&option_vi, index);
bool drawBar = false;
struct timeline_span span_px = index.data(_dataRole).value<struct timeline_span>();
if (_dataRole == ATapDataModel::TIMELINE_DATA) {
double span_s = span_px.maxRelTime - span_px.minRelTime;
if (qobject_cast<QTreeView *>(parent()) == nullptr)
return;
QTreeView * tree = qobject_cast<QTreeView *>(parent());
int start_px = tree->columnWidth(span_px.colStart);
int column_px = start_px + tree->columnWidth(span_px.colDuration);
if (tree) {
QAbstractProxyModel * proxy = qobject_cast<QAbstractProxyModel *>(tree->model());
if (proxy && proxy->sourceModel()) {
QModelIndex indexStart = proxy->mapFromSource(proxy->sourceModel()->index(0, span_px.colStart));
int colStart = -1;
int start_px = 0;
if (indexStart.isValid()) {
colStart = indexStart.column();
start_px = tree->columnWidth(colStart);
}
int colDuration = -1;
int column_px = start_px;
QModelIndex indexDuration = proxy->mapFromSource(proxy->sourceModel()->index(0, span_px.colDuration));
if (indexDuration.isValid()) {
colDuration = indexDuration.column();
column_px += tree->columnWidth(colDuration);
}
span_px.start = ((span_px.startTime - span_px.minRelTime) * column_px) / span_s;
span_px.width = ((span_px.stopTime - span_px.startTime) * column_px) / span_s;
span_px.start = ((span_px.startTime - span_px.minRelTime) * column_px) / span_s;
span_px.width = ((span_px.stopTime - span_px.startTime) * column_px) / span_s;
if (index.column() == span_px.colDuration) {
span_px.start -= start_px;
if (index.column() == colStart) {
drawBar = true;
} else if (index.column() == colDuration) {
drawBar = true;
span_px.start -= start_px;
}
}
}
}
if (!drawBar) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
// Paint our rect with no text using the current style, then draw our
// bar and text over it.
QStyledItemDelegate::paint(painter, option, index);
option_vi.text = QString();
QStyle *style = option_vi.widget ? option_vi.widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &option_vi, painter, option_vi.widget);
if (QApplication::style()->objectName().contains("vista")) {
// QWindowsVistaStyle::drawControl does this internally. Unfortunately there

View File

@ -53,9 +53,6 @@ class TimelineDelegate : public QStyledItemDelegate
public:
TimelineDelegate(QWidget *parent = 0);
// Make sure QStyledItemDelegate::paint doesn't draw any text.
virtual QString displayText(const QVariant &, const QLocale &) const { return QString(); }
void setDataRole(int role);
protected:

View File

@ -69,6 +69,7 @@ TrafficTab::TrafficTab(QWidget * parent) :
DetachableTabWidget(parent)
{
_createModel = nullptr;
_createDelegate = nullptr;
_disableTaps = false;
_nameResolution = false;
setTabBasename(QString());
@ -90,29 +91,19 @@ void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficLi
setOpenTabs(trafficList->protocols(true));
}
void TrafficTab::setDelegate(int column, ATapCreateDelegate createDelegate)
void TrafficTab::setDelegate(ATapCreateDelegate createDelegate)
{
if (! createDelegate || column < 0)
if (! createDelegate)
return;
if (_createDelegates.keys().contains(column))
_createDelegates.remove(column);
_createDelegates.insert(column, createDelegate);
_createDelegate = createDelegate;
for (int idx = 0; idx < count(); idx++) {
int setColumn = column;
ATapDataModel * model = modelForTabIndex(idx);
if (model->portsAreHidden()) {
if (model->modelType() == ATapDataModel::DATAMODEL_ENDPOINT && column > EndpointDataModel::ENDP_COLUMN_PORT)
setColumn -= 1;
else if (model->modelType() == ATapDataModel::DATAMODEL_CONVERSATION && column > ConversationDataModel::CONV_COLUMN_DST_PORT)
setColumn -= 2;
}
if (qobject_cast<QTreeView *>(widget(idx)))
{
QTreeView * tree = qobject_cast<QTreeView *>(widget(idx));
tree->setItemDelegateForColumn(setColumn, createDelegate(tree));
tree->setItemDelegate(createDelegate(tree));
}
}
}
@ -128,13 +119,9 @@ QTreeView * TrafficTab::createTree(int protoId)
model->enableTap();
foreach(int col, _createDelegates.keys())
if (_createDelegate)
{
if (_createDelegates[col])
{
ATapCreateDelegate creator = _createDelegates[col];
tree->setItemDelegateForColumn(col, creator(tree));
}
tree->setItemDelegate(_createDelegate(tree));
}
TrafficDataFilterProxy * proxyModel = new TrafficDataFilterProxy(tree);

View File

@ -97,15 +97,14 @@ public:
void setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel);
/**
* @brief Set the Delegate object for a specific column
* @brief Set the Delegate object for the tab. It will apply for all
* models residing in this tab object
*
* @param column the column to set the delegate for. It will apply for all models
* residing inside this tab object
* @param createDelegate the callback for the delegate creation
*
* @see ATapCreateDelegate
*/
void setDelegate(int column, ATapCreateDelegate createDelegate);
void setDelegate(ATapCreateDelegate createDelegate);
/**
* @brief Set the filter or remove it by providing an empty filter
@ -221,7 +220,7 @@ private:
QList<int> _allProtocols;
QMap<int, int> _tabs;
ATapModelCallback _createModel;
QMap<int, ATapCreateDelegate> _createDelegates;
ATapCreateDelegate _createDelegate;
GList ** _recentColumnList;
bool _disableTaps;

View File

@ -22,6 +22,7 @@
#include <QTreeView>
#include <QMenu>
#include <QHeaderView>
#include <QSortFilterProxyModel>
#include <QWidgetAction>
#include <QLineEdit>