forked from osmocom/wireshark
Qt: Add extra related packet indicator types.
Add the ability to set frame number types: none, request, or response. Use the types to draw different related packet indicators in the packet list. Track the conversation in PacketListRecord. Use it to draw dashed lines for unrelated frames. Set frame number types for DNS and ICMP. Instead of drawing a transparent QImage, alpha blend our foreground color and draw directly in our painter. Blend more toward the foreground color. Add FRAMENUM_TYPE to checkAPIs. Change-Id: I2495945bb436413e05d6ec697184a0b4fd5ad214 Reviewed-on: https://code.wireshark.org/review/7436 Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
parent
a065fefe68
commit
f2b35a180f
|
@ -5177,12 +5177,12 @@ proto_register_dns(void)
|
|||
|
||||
{ &hf_dns_response_in,
|
||||
{ "Response In", "dns.response_in",
|
||||
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
||||
FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
|
||||
"The response to this DNS query is in this frame", HFILL }},
|
||||
|
||||
{ &hf_dns_response_to,
|
||||
{ "Request In", "dns.response_to",
|
||||
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
||||
FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
|
||||
"This is a response to the DNS query in this frame", HFILL }},
|
||||
|
||||
{ &hf_dns_time,
|
||||
|
|
|
@ -1872,7 +1872,7 @@ void proto_register_icmp(void)
|
|||
|
||||
{&hf_icmp_resp_in,
|
||||
{"Response frame", "icmp.resp_in", FT_FRAMENUM, BASE_NONE,
|
||||
NULL, 0x0,
|
||||
FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
|
||||
"The frame number of the corresponding response",
|
||||
HFILL}},
|
||||
|
||||
|
@ -1884,7 +1884,7 @@ void proto_register_icmp(void)
|
|||
|
||||
{&hf_icmp_resp_to,
|
||||
{"Request frame", "icmp.resp_to", FT_FRAMENUM, BASE_NONE,
|
||||
NULL, 0x0,
|
||||
FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
|
||||
"The frame number of the corresponding request", HFILL}},
|
||||
|
||||
{&hf_icmp_resptime,
|
||||
|
|
|
@ -97,6 +97,14 @@ enum ftenum {
|
|||
|
||||
typedef enum ftenum ftenum_t;
|
||||
|
||||
enum ft_framenum_type {
|
||||
FT_FRAMENUM_NONE,
|
||||
FT_FRAMENUM_REQUEST,
|
||||
FT_FRAMENUM_RESPONSE
|
||||
};
|
||||
|
||||
typedef enum ft_framenum_type ft_framenum_type_t;
|
||||
|
||||
struct _ftype_t;
|
||||
typedef struct _ftype_t ftype_t;
|
||||
|
||||
|
|
|
@ -5636,8 +5636,9 @@ tmp_fld_check_assert(header_field_info *hfinfo)
|
|||
(hfinfo->type == FT_INT56) ||
|
||||
(hfinfo->type == FT_INT64) ||
|
||||
(hfinfo->type == FT_BOOLEAN) ||
|
||||
(hfinfo->type == FT_PROTOCOL) ))
|
||||
g_error("Field '%s' (%s) has a 'strings' value but is of type %s"
|
||||
(hfinfo->type == FT_PROTOCOL) ||
|
||||
(hfinfo->type == FT_FRAMENUM) ))
|
||||
g_error("Field '%s' (%s) has a 'strings' value but is of type %s"
|
||||
" (which is not allowed to have strings)\n",
|
||||
hfinfo->name, hfinfo->abbrev, ftype_name(hfinfo->type));
|
||||
|
||||
|
@ -6637,7 +6638,7 @@ fill_label_number(field_info *fi, gchar *label_str, gboolean is_signed)
|
|||
fmtfunc(tmp, value);
|
||||
label_fill(label_str, 0, hfinfo, tmp);
|
||||
}
|
||||
else if (hfinfo->strings) {
|
||||
else if (hfinfo->strings && hfinfo->type != FT_FRAMENUM) { /* Add fill_label_framenum? */
|
||||
const char *val_str = hf_try_val_to_str_const(value, hfinfo, "Unknown");
|
||||
|
||||
out = hfinfo_number_vals_format(hfinfo, buf, value);
|
||||
|
|
|
@ -98,6 +98,9 @@ typedef void (*custom_fmt_func_64_t)(gchar *, guint64);
|
|||
* header_field_info.strings */
|
||||
#define RVALS(x) (const struct _range_string*)(x)
|
||||
|
||||
/** Cast a ft_framenum_type_t, used to set header_field_info.strings */
|
||||
#define FRAMENUM_TYPE(x) GINT_TO_POINTER(x)
|
||||
|
||||
struct _protocol;
|
||||
|
||||
/** Structure for information about a protocol */
|
||||
|
|
|
@ -1817,8 +1817,8 @@ sub check_hf_entries($$)
|
|||
print STDERR "Error: $hf is passing the address of a pointer to RVALS in $filename\n";
|
||||
$errorCount++;
|
||||
}
|
||||
if ($convert !~ m/^((0[xX]0?)?0$|NULL$|VALS|VALS64|RVALS|TFS|CF_FUNC|&)/ && $display !~ /BASE_CUSTOM/) {
|
||||
print STDERR "Error: non-null $hf 'convert' field missing 'VALS|VALS64|RVALS|TFS|CF_FUNC|&' in $filename ?\n";
|
||||
if ($convert !~ m/^((0[xX]0?)?0$|NULL$|VALS|VALS64|RVALS|TFS|CF_FUNC|FRAMENUM_TYPE|&)/ && $display !~ /BASE_CUSTOM/) {
|
||||
print STDERR "Error: non-null $hf 'convert' field missing 'VALS|VALS64|RVALS|TFS|CF_FUNC|FRAMENUM_TYPE|&' in $filename ?\n";
|
||||
$errorCount++;
|
||||
}
|
||||
## Benign...
|
||||
|
|
|
@ -85,6 +85,7 @@ set(WIRESHARK_QT_HEADERS
|
|||
protocol_hierarchy_dialog.h
|
||||
qcustomplot.h
|
||||
recent_file_status.h
|
||||
related_packet_delegate.h
|
||||
rtp_stream_dialog.h
|
||||
sctp_all_assocs_dialog.h
|
||||
sctp_assoc_analyse_dialog.h
|
||||
|
@ -119,7 +120,6 @@ endif()
|
|||
file(GLOB EXTRA_QT_HEADERS
|
||||
packet_list_record.h
|
||||
qt_ui_utils.h
|
||||
related_packet_delegate.h
|
||||
sparkline_delegate.h
|
||||
stock_icon.h
|
||||
)
|
||||
|
|
|
@ -425,7 +425,8 @@ void PacketList::setProtoTree (ProtoTree *proto_tree) {
|
|||
proto_tree_ = proto_tree;
|
||||
|
||||
connect(proto_tree_, SIGNAL(goToPacket(int)), this, SLOT(goToPacket(int)));
|
||||
connect(proto_tree_, SIGNAL(relatedFrame(int)), this, SLOT(addRelatedFrame(int)));
|
||||
connect(proto_tree_, SIGNAL(relatedFrame(int,ft_framenum_type_t)),
|
||||
&related_packet_delegate_, SLOT(addRelatedFrame(int,ft_framenum_type_t)));
|
||||
}
|
||||
|
||||
void PacketList::setByteViewTab (ByteViewTab *byte_view_tab) {
|
||||
|
@ -456,12 +457,13 @@ void PacketList::selectionChanged (const QItemSelection & selected, const QItemS
|
|||
if (!cap_file_->edt) return;
|
||||
|
||||
if (proto_tree_ && cap_file_->edt->tree) {
|
||||
proto_tree_->fillProtocolTree(cap_file_->edt->tree);
|
||||
packet_info *pi = &cap_file_->edt->pi;
|
||||
related_packet_delegate_.setCurrentFrame(pi->fd->num);
|
||||
proto_tree_->fillProtocolTree(cap_file_->edt->tree);
|
||||
conversation_t *conv = find_conversation(pi->fd->num, &pi->src, &pi->dst, pi->ptype,
|
||||
pi->srcport, pi->destport, 0);
|
||||
if (conv) {
|
||||
related_packet_delegate_.setConversationSpan(conv->setup_frame, conv->last_frame);
|
||||
related_packet_delegate_.setConversation(conv);
|
||||
}
|
||||
viewport()->update();
|
||||
}
|
||||
|
@ -1038,11 +1040,6 @@ void PacketList::unsetAllTimeReferences()
|
|||
redrawVisiblePackets();
|
||||
}
|
||||
|
||||
void PacketList::addRelatedFrame(int related_frame)
|
||||
{
|
||||
related_packet_delegate_.addRelatedFrame(related_frame);
|
||||
}
|
||||
|
||||
void PacketList::showHeaderMenu(QPoint pos)
|
||||
{
|
||||
header_ctx_column_ = header()->logicalIndexAt(pos);
|
||||
|
|
|
@ -130,7 +130,6 @@ public slots:
|
|||
void applyRecentColumnWidths();
|
||||
|
||||
private slots:
|
||||
void addRelatedFrame(int related_frame);
|
||||
void showHeaderMenu(QPoint pos);
|
||||
void headerMenuTriggered();
|
||||
void columnVisibilityTriggered();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <epan/epan_dissect.h>
|
||||
#include <epan/column-info.h>
|
||||
#include <epan/column.h>
|
||||
#include <epan/conversation.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "color_filters.h"
|
||||
|
@ -39,7 +40,8 @@ unsigned PacketListRecord::col_data_ver_ = 1;
|
|||
PacketListRecord::PacketListRecord(frame_data *frameData) :
|
||||
fdata_(frameData),
|
||||
data_ver_(0),
|
||||
colorized_(false)
|
||||
colorized_(false),
|
||||
conv_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -159,6 +161,10 @@ void PacketListRecord::dissect(capture_file *cap_file, bool dissect_color)
|
|||
}
|
||||
data_ver_ = col_data_ver_;
|
||||
|
||||
packet_info *pi = &edt.pi;
|
||||
conv_ = find_conversation(pi->fd->num, &pi->src, &pi->dst, pi->ptype,
|
||||
pi->srcport, pi->destport, 0);
|
||||
|
||||
epan_dissect_cleanup(&edt);
|
||||
ws_buffer_free(&buf);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include <QList>
|
||||
#include <QVariant>
|
||||
|
||||
struct conversation;
|
||||
|
||||
class PacketListRecord
|
||||
{
|
||||
public:
|
||||
|
@ -44,6 +46,7 @@ public:
|
|||
frame_data *frameData() const { return fdata_; }
|
||||
// packet_list->col_to_text in gtk/packet_list_store.c
|
||||
static int textColumn(int column) { return cinfo_column_.value(column, -1); }
|
||||
struct conversation *conversation() { return conv_; }
|
||||
|
||||
int columnTextSize(const char *str);
|
||||
static void resetColumns(column_info *cinfo);
|
||||
|
@ -63,6 +66,9 @@ private:
|
|||
/** Has this record been colorized? */
|
||||
bool colorized_;
|
||||
|
||||
/** Conversation. Used by RelatedPacketDelegate */
|
||||
struct conversation *conv_;
|
||||
|
||||
void dissect(capture_file *cap_file, bool dissect_color = false);
|
||||
void cacheColumnStrings(column_info *cinfo);
|
||||
|
||||
|
|
|
@ -107,7 +107,8 @@ proto_tree_draw_node(proto_node *node, gpointer data)
|
|||
item->setData(0, Qt::FontRole, font);
|
||||
|
||||
if (fi->hfinfo->type == FT_FRAMENUM) {
|
||||
proto_tree->emitRelatedFrame(fi->value.value.uinteger);
|
||||
ft_framenum_type_t framenum_type = (ft_framenum_type_t)GPOINTER_TO_INT(fi->hfinfo->strings);
|
||||
proto_tree->emitRelatedFrame(fi->value.value.uinteger, framenum_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,9 +295,9 @@ void ProtoTree::fillProtocolTree(proto_tree *protocol_tree) {
|
|||
proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, invisibleRootItem());
|
||||
}
|
||||
|
||||
void ProtoTree::emitRelatedFrame(int related_frame)
|
||||
void ProtoTree::emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type)
|
||||
{
|
||||
emit relatedFrame(related_frame);
|
||||
emit relatedFrame(related_frame, framenum_type);
|
||||
}
|
||||
|
||||
void ProtoTree::updateSelectionStatus(QTreeWidgetItem* item) {
|
||||
|
|
|
@ -37,7 +37,7 @@ class ProtoTree : public QTreeWidget
|
|||
public:
|
||||
explicit ProtoTree(QWidget *parent = 0);
|
||||
void fillProtocolTree(proto_tree *protocol_tree);
|
||||
void emitRelatedFrame(int related_frame);
|
||||
void emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE);
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
|
@ -53,7 +53,7 @@ signals:
|
|||
void protoItemSelected(field_info *);
|
||||
void openPacketInNewWindow(bool);
|
||||
void goToPacket(int);
|
||||
void relatedFrame(int);
|
||||
void relatedFrame(int, ft_framenum_type_t);
|
||||
|
||||
public slots:
|
||||
void setMonospaceFont(const QFont &mono_font);
|
||||
|
|
|
@ -22,22 +22,45 @@
|
|||
#include "related_packet_delegate.h"
|
||||
#include "packet_list_record.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include "color_utils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
|
||||
// To do:
|
||||
// - Add other frame types and symbols (ACKs, etc).
|
||||
// - Add tooltips. It looks like this needs to be done in
|
||||
// PacketListModel::data.
|
||||
// - Add "Go -> Next Related" and "Go -> Previous Related"?
|
||||
// - Apply as filter?
|
||||
|
||||
RelatedPacketDelegate::RelatedPacketDelegate(QWidget *parent) :
|
||||
QStyledItemDelegate(parent),
|
||||
conv_(NULL),
|
||||
current_frame_(0)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void RelatedPacketDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
int en_w = option.fontMetrics.height() / 2;
|
||||
|
||||
QStyleOptionViewItemV4 optv4 = option;
|
||||
QStyledItemDelegate::initStyleOption(&optv4, index);
|
||||
int em_w = optv4.fontMetrics.height();
|
||||
int en_w = (em_w + 1) / 2;
|
||||
|
||||
optv4.features |= QStyleOptionViewItemV4::HasDecoration;
|
||||
optv4.decorationSize.setHeight(1);
|
||||
optv4.decorationSize.setWidth(en_w);
|
||||
optv4.decorationSize.setWidth(em_w);
|
||||
QStyledItemDelegate::paint(painter, optv4, index);
|
||||
|
||||
guint32 setup_frame = 0, last_frame = 0;
|
||||
if (conv_) {
|
||||
setup_frame = (int) conv_->setup_frame;
|
||||
last_frame = (int) conv_->last_frame;
|
||||
}
|
||||
|
||||
const frame_data *fd;
|
||||
PacketListRecord *record = static_cast<PacketListRecord*>(index.internalPointer());
|
||||
if (!record || (fd = record->frameData()) == NULL) {
|
||||
|
@ -62,41 +85,78 @@ void RelatedPacketDelegate::paint(QPainter *painter, const QStyleOptionViewItem
|
|||
} else {
|
||||
fg = optv4.palette.color(cg, QPalette::Text);
|
||||
}
|
||||
qreal alpha = 0.20; // Arbitrary. Should arguably be a preference.
|
||||
|
||||
// We draw in the same place more than once so we first draw on a
|
||||
// QImage at 100% opacity then draw that on our packet list item.
|
||||
QImage overlay = QImage(en_w * 2, optv4.rect.height(), QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter op(&overlay);
|
||||
fg = ColorUtils::alphaBlend(fg, optv4.palette.color(cg, QPalette::Base), 0.5);
|
||||
QPen line_pen(fg);
|
||||
line_pen.setWidth(optv4.fontMetrics.lineWidth());
|
||||
line_pen.setJoinStyle(Qt::RoundJoin);
|
||||
|
||||
overlay.fill(Qt::transparent);
|
||||
op.setPen(fg);
|
||||
op.translate(en_w + 0.5, 0.5);
|
||||
op.setRenderHint(QPainter::Antialiasing, true);
|
||||
painter->setPen(line_pen);
|
||||
painter->translate(optv4.rect.x(), optv4.rect.y());
|
||||
painter->translate(en_w + 0.5, 0.5);
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
int height = optv4.rect.height();
|
||||
|
||||
// Uncomment to make the boundary visible.
|
||||
// painter->save();
|
||||
// painter->setPen(Qt::darkRed);
|
||||
// painter->drawRect(QRectF(0.5, 0.5, en_w - 1, height - 1));
|
||||
// painter->restore();
|
||||
|
||||
// The current decorations are based on what looked good and were easy
|
||||
// to code. W might want to improve them by drawing small dots or tick
|
||||
// marks for frames in the same conversation XOR draw a gap for unrelated
|
||||
// frames.
|
||||
if (first_frame_ > 0 && last_frame_ > 0 && first_frame_ != last_frame_) {
|
||||
int height = optv4.rect.height();
|
||||
if ((int) fd->num == first_frame_) {
|
||||
op.drawLine(0, height / 2, 0, height);
|
||||
op.drawLine(1, height / 2, en_w, height / 2);
|
||||
} else if ((int) fd->num > first_frame_ && (int) fd->num < last_frame_) {
|
||||
op.drawLine(0, 0, 0, height);
|
||||
} else if ((int) fd->num == last_frame_) {
|
||||
op.drawLine(0, 0, 0, height / 2);
|
||||
op.drawLine(1, height / 2, en_w, height / 2);
|
||||
// to code.
|
||||
|
||||
// Vertical line. Lower and upper half for the start and end of the
|
||||
// conversation respectively, solid for conversation member, dashed
|
||||
// for other packets in the start-end range.
|
||||
if (setup_frame > 0 && last_frame > 0 && setup_frame != last_frame) {
|
||||
if (fd->num == setup_frame) {
|
||||
painter->drawLine(0, height / 2, 0, height);
|
||||
painter->drawLine(1, height / 2, en_w - 1, height / 2);
|
||||
} else if (fd->num > setup_frame && fd->num < last_frame) {
|
||||
painter->save();
|
||||
if (conv_ != record->conversation()) {
|
||||
QPen other_pen(line_pen);
|
||||
other_pen.setStyle(Qt::DashLine);
|
||||
painter->setPen(other_pen);
|
||||
}
|
||||
painter->drawLine(0, 0, 0, height);
|
||||
painter->restore();
|
||||
} else if (fd->num == last_frame) {
|
||||
painter->drawLine(0, 0, 0, height / 2);
|
||||
painter->drawLine(1, height / 2, en_w, height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Related packet indicator. Rightward arrow for requests, leftward
|
||||
// arrow for responses, circle for others.
|
||||
if (related_frames_.contains(fd->num)) {
|
||||
op.setBrush(fg);
|
||||
op.drawEllipse(QPointF(0.0, optv4.rect.height() / 2), 2, 2);
|
||||
painter->setBrush(fg);
|
||||
switch (related_frames_[fd->num]) {
|
||||
// Request and response arrows are moved forward one pixel in order to
|
||||
// maximize white space between the heads and the conversation line.
|
||||
case FT_FRAMENUM_REQUEST:
|
||||
{
|
||||
int hh = height / 2;
|
||||
QPoint tail(2 - en_w, hh);
|
||||
QPoint head(en_w, hh);
|
||||
drawArrow(painter, tail, head, hh / 2);
|
||||
break;
|
||||
}
|
||||
case FT_FRAMENUM_RESPONSE:
|
||||
{
|
||||
int hh = height / 2;
|
||||
QPoint tail(en_w - 1, hh);
|
||||
QPoint head(1 - en_w, hh);
|
||||
drawArrow(painter, tail, head, hh / 2);
|
||||
break;
|
||||
}
|
||||
case FT_FRAMENUM_NONE:
|
||||
default:
|
||||
painter->drawEllipse(QPointF(0.0, optv4.rect.height() / 2), 2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
painter->setOpacity(alpha);
|
||||
painter->drawImage(optv4.rect.x(), optv4.rect.y(), overlay);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
|
@ -106,21 +166,47 @@ QSize RelatedPacketDelegate::sizeHint(const QStyleOptionViewItem &option,
|
|||
QStyledItemDelegate::sizeHint(option, index).height());
|
||||
}
|
||||
|
||||
void RelatedPacketDelegate::drawArrow(QPainter *painter, QPoint tail, QPoint head, int head_size) const
|
||||
{
|
||||
int x_mul = head.x() > tail.x() ? -1 : 1;
|
||||
QPoint head_points[] = {
|
||||
head,
|
||||
QPoint(head.x() + (head_size * x_mul), head.y() + (head_size / 2)),
|
||||
QPoint(head.x() + (head_size * x_mul), head.y() - (head_size / 2)),
|
||||
};
|
||||
|
||||
painter->drawLine(tail.x(), tail.y(), head.x() + (head_size * x_mul), head.y());
|
||||
painter->drawPolygon(head_points, 3);
|
||||
}
|
||||
|
||||
void RelatedPacketDelegate::clear()
|
||||
{
|
||||
related_frames_.clear();
|
||||
first_frame_ = last_frame_ = -1;
|
||||
current_frame_ = 0;
|
||||
conv_ = NULL;
|
||||
}
|
||||
|
||||
void RelatedPacketDelegate::addRelatedFrame(int frame_num)
|
||||
void RelatedPacketDelegate::addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type)
|
||||
{
|
||||
related_frames_ << frame_num;
|
||||
related_frames_[frame_num] = framenum_type;
|
||||
// Last match wins. Last match might not make sense, however.
|
||||
if (current_frame_ > 0) {
|
||||
switch (framenum_type) {
|
||||
case FT_FRAMENUM_REQUEST:
|
||||
related_frames_[current_frame_] = FT_FRAMENUM_RESPONSE;
|
||||
break;
|
||||
case FT_FRAMENUM_RESPONSE:
|
||||
related_frames_[current_frame_] = FT_FRAMENUM_REQUEST;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RelatedPacketDelegate::setConversationSpan(int first_frame, int last_frame)
|
||||
void RelatedPacketDelegate::setConversation(conversation *conv)
|
||||
{
|
||||
first_frame_ = first_frame;
|
||||
last_frame_ = last_frame;
|
||||
conv_ = conv;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -26,16 +26,23 @@
|
|||
|
||||
#include "epan/conversation.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class QPainter;
|
||||
struct conversation;
|
||||
|
||||
class RelatedPacketDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RelatedPacketDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) { clear(); }
|
||||
RelatedPacketDelegate(QWidget *parent = 0);
|
||||
void clear();
|
||||
void addRelatedFrame(int frame_num);
|
||||
void setConversationSpan(int first_frame, int last_frame);
|
||||
void setCurrentFrame(guint32 current_frame) { current_frame_ = current_frame; }
|
||||
void setConversation(struct conversation *conv);
|
||||
|
||||
public slots:
|
||||
void addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE);
|
||||
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
|
@ -44,10 +51,11 @@ protected:
|
|||
const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
QList<int> related_frames_;
|
||||
int first_frame_;
|
||||
int last_frame_;
|
||||
QHash<int, ft_framenum_type_t> related_frames_;
|
||||
struct conversation *conv_;
|
||||
guint32 current_frame_;
|
||||
|
||||
void drawArrow(QPainter *painter, QPoint tail, QPoint head, int head_size) const;
|
||||
signals:
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue