Qt: ByteViewText hover information.

When the user hovers over a byte view field, highlight it and show a
description in the status bar.

Add a "byte" status bar context and fix a label stack pop bug.

Keep proto_find_field_from_offset from matching generated items.
Otherwise hovering and selecting finds things like GeoIP entries and
checksum validation information. This affects the GTK+ UI as well.

Change-Id: Ic81c0d8159510a72d30c41f961807d8a48d05e16
Reviewed-on: https://code.wireshark.org/review/4943
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2014-10-26 18:48:14 -07:00 committed by Gerald Combs
parent c33deaa92f
commit 1c159818fd
10 changed files with 131 additions and 51 deletions

View File

@ -6658,7 +6658,7 @@ check_for_offset(proto_node *node, const gpointer data)
offset_search_t *offsearch = (offset_search_t *)data;
/* !fi == the top most container node which holds nothing */
if (fi && !PROTO_ITEM_IS_HIDDEN(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) {
if (fi && !PROTO_ITEM_IS_HIDDEN(node) && !PROTO_ITEM_IS_GENERATED(node) && fi->ds_tvb && offsearch->tvb == fi->ds_tvb) {
if (offsearch->offset >= (guint) fi->start &&
offsearch->offset < (guint) (fi->start + fi->length)) {

View File

@ -37,6 +37,7 @@ void ByteViewTab::addTab(const char *name, tvbuff_t *tvb, proto_tree *tree, QTre
byte_view_text->setAccessibleName(name);
byte_view_text->setMonospaceFont(mono_font_);
connect(this, SIGNAL(monospaceFontChanged(QFont)), byte_view_text, SLOT(setMonospaceFont(QFont)));
connect(byte_view_text, SIGNAL(byteFieldHovered(QString&)), this, SIGNAL(byteFieldHovered(QString&)));
QTabWidget::addTab(byte_view_text, name);
}

View File

@ -52,6 +52,7 @@ protected:
signals:
void monospaceFontChanged(const QFont &mono_font);
void byteFieldHovered(QString &);
public slots:
void protoTreeItemChanged(QTreeWidgetItem *current);

View File

@ -48,17 +48,15 @@ ByteViewText::ByteViewText(QWidget *parent, tvbuff_t *tvb, proto_tree *tree, QTr
bold_highlight_(false),
encoding_(encoding),
format_(BYTES_HEX),
p_start_(-1),
p_end_(-1),
f_start_(0),
f_end_(0),
fa_start_(-1),
fa_end_(-1),
p_bound_(0, 0),
f_bound_(0, 0),
fa_bound_(0, 0),
show_offset_(true),
show_hex_(true),
show_ascii_(true),
row_width_(16)
{
setMouseTracking(true);
}
void ByteViewText::setEncoding(packet_char_enc encoding)
@ -75,8 +73,8 @@ bool ByteViewText::hasDataSource(const tvbuff_t *ds_tvb) {
void ByteViewText::setProtocolHighlight(int start, int end)
{
p_start_ = qMax(0, start);
p_end_ = qMax(0, end);
p_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
p_bound_save_ = p_bound_;
viewport()->update();
}
@ -84,15 +82,15 @@ void ByteViewText::setFieldHighlight(int start, int end, guint32 mask, int mask_
{
Q_UNUSED(mask);
Q_UNUSED(mask_le);
f_start_ = qMax(0, start);
f_end_ = qMax(0, end);
f_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
f_bound_save_ = f_bound_;
viewport()->update();
}
void ByteViewText::setFieldAppendixHighlight(int start, int end)
{
fa_start_ = qMax(0, start);
fa_end_ = qMax(0, end);
fa_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
fa_bound_save_ = f_bound_;
viewport()->update();
}
@ -176,25 +174,12 @@ void ByteViewText::resizeEvent(QResizeEvent *)
}
void ByteViewText::mousePressEvent (QMouseEvent *event) {
if (!tvb_ || event->button() != Qt::LeftButton ) {
if (!tvb_ || !event || event->button() != Qt::LeftButton ) {
return;
}
QPoint pt = event->pos();
int byte = (verticalScrollBar()->value() + (pt.y() / line_spacing_)) * row_width_;
int x = (horizontalScrollBar()->value() * font_width_) + pt.x();
int col = x_pos_to_column_.value(x, -1);
if (col < 0) {
return;
}
byte += col;
if ((guint) byte > tvb_captured_length(tvb_)) {
return;
}
field_info *fi = proto_find_field_from_offset(proto_tree_, byte, tvb_);
QPoint pos = event->pos();
field_info *fi = fieldAtPixel(pos);
if (fi && tree_widget_) {
// XXX - This should probably be a ProtoTree method.
@ -209,6 +194,54 @@ void ByteViewText::mousePressEvent (QMouseEvent *event) {
}
}
void ByteViewText::mouseMoveEvent(QMouseEvent *event)
{
QString field_str;
if (!event) {
emit byteFieldHovered(field_str);
p_bound_ = p_bound_save_;
f_bound_ = f_bound_save_;
fa_bound_ = fa_bound_save_;
viewport()->update();
return;
}
QPoint pos = event->pos();
field_info *fi = fieldAtPixel(pos);
if (fi) {
if (fi->length < 2) {
field_str = QString(tr("Byte %1"))
.arg(fi->start);
} else {
field_str = QString(tr("Bytes %1-%2"))
.arg(fi->start)
.arg(fi->start + fi->length - 1);
}
field_str += QString(": %1 (%2)")
.arg(fi->hfinfo->name)
.arg(fi->hfinfo->abbrev);
f_bound_ = QPair<guint, guint>(fi->start, fi->start + fi->length);
p_bound_ = QPair<guint, guint>(0, 0);
fa_bound_ = QPair<guint, guint>(0, 0);
} else {
p_bound_ = p_bound_save_;
f_bound_ = f_bound_save_;
fa_bound_ = fa_bound_save_;
}
emit byteFieldHovered(field_str);
viewport()->update();
}
void ByteViewText::leaveEvent(QEvent *event)
{
QString empty;
emit byteFieldHovered(empty);
p_bound_ = p_bound_save_;
f_bound_ = f_bound_save_;
fa_bound_ = fa_bound_save_;
viewport()->update();
QAbstractScrollArea::leaveEvent(event);
}
// Private
const int ByteViewText::separator_interval_ = 8; // Insert a space after this many bytes
@ -241,10 +274,10 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
highlight_state hex_state = StateNormal;
bool add_space = tvb_pos != offset;
if ((tvb_pos >= f_start_ && tvb_pos < f_end_) || (tvb_pos >= fa_start_ && tvb_pos < fa_end_)) {
if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
hex_state = StateField;
offset_state = StateOffsetField;
} else if (tvb_pos >= p_start_ && tvb_pos < p_end_) {
} else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
hex_state = StateProtocol;
}
@ -291,10 +324,10 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
highlight_state ascii_state = StateNormal;
bool add_space = tvb_pos != offset;
if ((tvb_pos >= f_start_ && tvb_pos < f_end_) || (tvb_pos >= fa_start_ && tvb_pos < fa_end_)) {
if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
ascii_state = StateField;
offset_state = StateOffsetField;
} else if (tvb_pos >= p_start_ && tvb_pos < p_end_) {
} else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
ascii_state = StateProtocol;
}
@ -433,6 +466,24 @@ void ByteViewText::updateScrollbars()
horizontalScrollBar()->setRange(0, qMax(0, static_cast<int>((totalPixels() - viewport()->width()) / font_width_)));
}
field_info *ByteViewText::fieldAtPixel(QPoint &pos)
{
int byte = (verticalScrollBar()->value() + (pos.y() / line_spacing_)) * row_width_;
int x = (horizontalScrollBar()->value() * font_width_) + pos.x();
int col = x_pos_to_column_.value(x, -1);
if (col < 0) {
return NULL;
}
byte += col;
if ((guint) byte > tvb_captured_length(tvb_)) {
return NULL;
}
return proto_find_field_from_offset(proto_tree_, byte, tvb_);
}
/*
* Editor modelines
*

View File

@ -53,14 +53,18 @@ public:
void setFieldHighlight(int start, int end, guint32 mask = 0, int mask_le = 0);
void setFieldAppendixHighlight(int start, int end);
signals:
void byteFieldHovered(QString &);
public slots:
void setMonospaceFont(const QFont &mono_font);
protected:
virtual void paintEvent(QPaintEvent *);
virtual void resizeEvent(QResizeEvent *);
void mousePressEvent (QMouseEvent * event);
virtual void mousePressEvent (QMouseEvent * event);
virtual void mouseMoveEvent (QMouseEvent * event);
virtual void leaveEvent(QEvent *event);
private:
typedef enum {
@ -80,6 +84,7 @@ private:
int asciiPixels();
int totalPixels();
void updateScrollbars();
field_info *fieldAtPixel(QPoint &pos);
static const int separator_interval_;
tvbuff_t *tvb_;
@ -94,14 +99,17 @@ private:
gboolean bold_highlight_;
/* data */
// Data
packet_char_enc encoding_; // ASCII or EBCDIC
bytes_view_type format_; // bytes in hex or bytes as bits
/* data-highlight */
guint p_start_, p_end_; // Protocol
guint f_start_, f_end_; // Field
guint fa_start_, fa_end_; // Field appendix
// Data highlight
QPair<guint,guint> p_bound_;
QPair<guint,guint> f_bound_;
QPair<guint,guint> fa_bound_;
QPair<guint,guint> p_bound_save_;
QPair<guint,guint> f_bound_save_;
QPair<guint,guint> fa_bound_save_;
bool show_offset_; // Should we show the byte offset?
bool show_hex_; // Should we show the hex display?

View File

@ -48,7 +48,7 @@ void LabelStack::setTemporaryContext(const int ctx) {
}
void LabelStack::fillLabel() {
StackItem *si;
StackItem si;
QString style_sheet;
style_sheet =
@ -62,7 +62,7 @@ void LabelStack::fillLabel() {
si = labels_.first();
if (si->ctx == temporary_ctx_) {
if (si.ctx == temporary_ctx_) {
style_sheet += QString(
" border-radius: 0.25em;"
" color: #%1;"
@ -74,23 +74,23 @@ void LabelStack::fillLabel() {
style_sheet += "}";
setStyleSheet(style_sheet);
setText(si->text);
setText(si.text);
}
void LabelStack::pushText(QString &text, int ctx) {
StackItem *si = new StackItem;
popText(ctx);
if (ctx == temporary_ctx_) {
temporary_timer_.stop();
popText(temporary_ctx_);
temporary_epoch_.start();
temporary_timer_.start(temporary_flash_timeout_);
emit toggleTemporaryFlash(true);
}
si->text = text;
si->ctx = ctx;
StackItem si;
si.text = text;
si.ctx = ctx;
labels_.prepend(si);
fillLabel();
}
@ -122,10 +122,10 @@ void LabelStack::contextMenuEvent(QContextMenuEvent *event)
}
void LabelStack::popText(int ctx) {
QMutableListIterator<StackItem *> iter(labels_);
QMutableListIterator<StackItem> iter(labels_);
while (iter.hasNext()) {
if (iter.next()->ctx == ctx) {
if (iter.next().ctx == ctx) {
iter.remove();
break;
}

View File

@ -49,7 +49,7 @@ private:
} StackItem;
int temporary_ctx_;
QList<StackItem *> labels_;
QList<StackItem> labels_;
QTime temporary_epoch_;
QTimer temporary_timer_;

View File

@ -46,6 +46,7 @@ enum StatusContext {
STATUS_CTX_MAIN,
STATUS_CTX_FILE,
STATUS_CTX_FIELD,
STATUS_CTX_BYTE,
STATUS_CTX_FILTER,
STATUS_CTX_TEMPORARY
};
@ -251,6 +252,20 @@ void MainStatusBar::popFieldStatus() {
info_status_.popText(STATUS_CTX_FIELD);
}
void MainStatusBar::pushByteStatus(QString &message)
{
if (message.isNull()) {
popByteStatus();
} else {
info_status_.pushText(message, STATUS_CTX_BYTE);
}
}
void MainStatusBar::popByteStatus()
{
info_status_.popText(STATUS_CTX_BYTE);
}
void MainStatusBar::pushFilterStatus(QString &message) {
info_status_.pushText(message, STATUS_CTX_FILTER);
expertUpdate();

View File

@ -63,6 +63,8 @@ public slots:
void popFileStatus();
void pushFieldStatus(QString &message);
void popFieldStatus();
void pushByteStatus(QString &message);
void popByteStatus();
void pushFilterStatus(QString &message);
void popFilterStatus();
void pushProfileName();

View File

@ -359,10 +359,12 @@ MainWindow::MainWindow(QWidget *parent) :
connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
this, SLOT(setMenusForSelectedTreeRow(field_info *)));
connect(byte_view_tab_, SIGNAL(byteFieldHovered(QString&)),
main_ui_->statusBar, SLOT(pushByteStatus(QString&)));
connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
this, SLOT(openCaptureFile(QString&)));