2012-01-04 22:13:01 +00:00
|
|
|
/* byte_view_text.cpp
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2012-06-28 22:56:06 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2012-01-04 22:13:01 +00:00
|
|
|
*/
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Some code based on QHexView by Even Teran
|
|
|
|
// https://code.google.com/p/qhexview/
|
|
|
|
|
2012-01-04 22:13:01 +00:00
|
|
|
#include "byte_view_text.h"
|
|
|
|
|
|
|
|
#include <epan/charsets.h>
|
|
|
|
|
2014-10-25 19:36:32 +00:00
|
|
|
#include "color_utils.h"
|
2012-10-30 19:21:24 +00:00
|
|
|
#include "wireshark_application.h"
|
2014-10-24 20:24:23 +00:00
|
|
|
|
2014-10-27 17:28:45 +00:00
|
|
|
#include <QActionGroup>
|
2012-01-04 22:13:01 +00:00
|
|
|
#include <QMouseEvent>
|
2014-10-24 20:24:23 +00:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QScrollBar>
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// To do:
|
2014-10-27 17:28:45 +00:00
|
|
|
// - Add recent settings and context menu items to show/hide the offset,
|
|
|
|
// hex/bits, and ASCII/EBCDIC.
|
|
|
|
// - Add a UTF-8 and possibly UTF-xx option to the ASCII display.
|
2014-10-25 19:36:32 +00:00
|
|
|
|
|
|
|
// We don't obey the gui.hex_dump_highlight_style preference. If you
|
|
|
|
// would like to add support for this you'll probably have to call
|
|
|
|
// QPainter::drawText for each individual character.
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-27 17:28:45 +00:00
|
|
|
Q_DECLARE_METATYPE(bytes_view_type)
|
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
ByteViewText::ByteViewText(QWidget *parent, tvbuff_t *tvb, proto_tree *tree, QTreeWidget *tree_widget, packet_char_enc encoding) :
|
2014-10-24 20:24:23 +00:00
|
|
|
QAbstractScrollArea(parent),
|
2012-10-30 19:21:24 +00:00
|
|
|
tvb_(tvb),
|
|
|
|
proto_tree_(tree),
|
|
|
|
tree_widget_(tree_widget),
|
|
|
|
bold_highlight_(false),
|
|
|
|
encoding_(encoding),
|
|
|
|
format_(BYTES_HEX),
|
2014-10-27 17:28:45 +00:00
|
|
|
format_actions_(new QActionGroup(this)),
|
2014-10-27 01:48:14 +00:00
|
|
|
p_bound_(0, 0),
|
|
|
|
f_bound_(0, 0),
|
|
|
|
fa_bound_(0, 0),
|
2014-10-24 20:24:23 +00:00
|
|
|
show_offset_(true),
|
|
|
|
show_hex_(true),
|
|
|
|
show_ascii_(true),
|
|
|
|
row_width_(16)
|
2012-01-04 22:13:01 +00:00
|
|
|
{
|
2014-10-27 17:28:45 +00:00
|
|
|
QAction *action;
|
|
|
|
|
|
|
|
action = format_actions_->addAction(tr("Show bytes as hexadecimal"));
|
|
|
|
action->setData(qVariantFromValue(BYTES_HEX));
|
|
|
|
action->setCheckable(true);
|
|
|
|
action->setChecked(true);
|
|
|
|
action = format_actions_->addAction(tr("Show bytes as bits"));
|
|
|
|
action->setData(qVariantFromValue(BYTES_BITS));
|
|
|
|
action->setCheckable(true);
|
|
|
|
|
|
|
|
ctx_menu_.addActions(format_actions_->actions());
|
|
|
|
ctx_menu_.addSeparator();
|
|
|
|
|
|
|
|
connect(format_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setHexDisplayFormat(QAction*)));
|
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
setMouseTracking(true);
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewText::setEncoding(packet_char_enc encoding)
|
|
|
|
{
|
|
|
|
encoding_ = encoding;
|
2014-10-25 19:36:32 +00:00
|
|
|
viewport()->update();
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
bool ByteViewText::hasDataSource(const tvbuff_t *ds_tvb) {
|
2012-10-30 19:21:24 +00:00
|
|
|
if (ds_tvb != NULL && ds_tvb == tvb_)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewText::setProtocolHighlight(int start, int end)
|
|
|
|
{
|
2014-10-27 01:48:14 +00:00
|
|
|
p_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
|
|
|
|
p_bound_save_ = p_bound_;
|
2014-10-24 20:24:23 +00:00
|
|
|
viewport()->update();
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewText::setFieldHighlight(int start, int end, guint32 mask, int mask_le)
|
|
|
|
{
|
|
|
|
Q_UNUSED(mask);
|
|
|
|
Q_UNUSED(mask_le);
|
2014-10-27 01:48:14 +00:00
|
|
|
f_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
|
|
|
|
f_bound_save_ = f_bound_;
|
2015-02-26 16:18:04 +00:00
|
|
|
scrollToByte(start);
|
2014-10-24 20:24:23 +00:00
|
|
|
viewport()->update();
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewText::setFieldAppendixHighlight(int start, int end)
|
|
|
|
{
|
2014-10-27 01:48:14 +00:00
|
|
|
fa_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
|
|
|
|
fa_bound_save_ = f_bound_;
|
2014-10-24 20:24:23 +00:00
|
|
|
viewport()->update();
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
void ByteViewText::setMonospaceFont(const QFont &mono_font)
|
2012-10-30 19:21:24 +00:00
|
|
|
{
|
2014-10-25 19:36:32 +00:00
|
|
|
mono_font_ = mono_font;
|
|
|
|
// mono_bold_font_ = QFont(mono_font);
|
|
|
|
// mono_bold_font_.setBold(true);
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
const QFontMetricsF fm(mono_font);
|
|
|
|
font_width_ = fm.width('M');
|
2014-10-25 19:36:32 +00:00
|
|
|
line_spacing_ = fm.lineSpacing() + 0.5;
|
2014-10-24 20:24:23 +00:00
|
|
|
one_em_ = fm.height();
|
2014-10-25 19:36:32 +00:00
|
|
|
margin_ = fm.height() / 2;
|
2014-10-24 20:24:23 +00:00
|
|
|
|
|
|
|
setFont(mono_font);
|
|
|
|
|
|
|
|
updateScrollbars();
|
2014-10-25 19:36:32 +00:00
|
|
|
viewport()->update();
|
2014-10-24 20:24:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ByteViewText::paintEvent(QPaintEvent *)
|
|
|
|
{
|
|
|
|
QPainter painter(viewport());
|
|
|
|
painter.translate(-horizontalScrollBar()->value() * font_width_, 0);
|
|
|
|
|
|
|
|
// Pixel offset of this row
|
|
|
|
int row_y = 0;
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Starting byte offset
|
|
|
|
guint offset = (guint) verticalScrollBar()->value() * row_width_;
|
2012-11-06 21:49:16 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Clear the area
|
|
|
|
painter.fillRect(viewport()->rect(), palette().base());
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Offset background
|
2014-10-26 00:19:38 +00:00
|
|
|
offset_normal_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35));
|
|
|
|
offset_field_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65));
|
2014-10-24 20:24:23 +00:00
|
|
|
if (show_offset_) {
|
|
|
|
QRect offset_rect = QRect(viewport()->rect());
|
|
|
|
offset_rect.setWidth(offsetPixels());
|
2014-10-26 00:19:38 +00:00
|
|
|
painter.fillRect(offset_rect, palette().window());
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (!tvb_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map window coordinates to byte offsets
|
|
|
|
x_pos_to_column_.clear();
|
|
|
|
for (guint i = 0; i < row_width_; i++) {
|
|
|
|
int sep_width = (i / separator_interval_) * font_width_;
|
|
|
|
if (show_hex_) {
|
2014-10-27 17:28:45 +00:00
|
|
|
// Hittable pixels extend 1/2 space on either side of the hex digits
|
|
|
|
int pixels_per_byte = (format_ == BYTES_HEX ? 3 : 9) * font_width_;
|
|
|
|
int hex_x = offsetPixels() + margin_ + sep_width + (i * pixels_per_byte) - (font_width_ / 2);
|
|
|
|
for (int j = 0; j <= pixels_per_byte; j++) {
|
2014-10-24 20:24:23 +00:00
|
|
|
x_pos_to_column_[hex_x + j] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (show_ascii_) {
|
|
|
|
int ascii_x = offsetPixels() + hexPixels() + margin_ + sep_width + (i * font_width_);
|
|
|
|
for (int j = 0; j <= font_width_; j++) {
|
|
|
|
x_pos_to_column_[ascii_x + j] = i;
|
|
|
|
}
|
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Data rows
|
|
|
|
int widget_height = height();
|
|
|
|
painter.save();
|
2014-10-25 19:36:32 +00:00
|
|
|
while(row_y + line_spacing_ < widget_height && offset < tvb_captured_length(tvb_)) {
|
2014-10-24 20:24:23 +00:00
|
|
|
drawOffsetLine(painter, offset, row_y);
|
|
|
|
offset += row_width_;
|
2014-10-25 19:36:32 +00:00
|
|
|
row_y += line_spacing_;
|
2014-10-24 20:24:23 +00:00
|
|
|
}
|
|
|
|
painter.restore();
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
void ByteViewText::resizeEvent(QResizeEvent *)
|
2014-09-23 20:35:10 +00:00
|
|
|
{
|
2014-10-24 20:24:23 +00:00
|
|
|
updateScrollbars();
|
2014-09-23 20:35:10 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
void ByteViewText::mousePressEvent (QMouseEvent *event) {
|
2014-10-27 01:48:14 +00:00
|
|
|
if (!tvb_ || !event || event->button() != Qt::LeftButton ) {
|
2014-10-24 20:24:23 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
QPoint pos = event->pos();
|
|
|
|
field_info *fi = fieldAtPixel(pos);
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (fi && tree_widget_) {
|
|
|
|
// XXX - This should probably be a ProtoTree method.
|
|
|
|
QTreeWidgetItemIterator iter(tree_widget_);
|
|
|
|
while (*iter) {
|
|
|
|
if (fi == (*iter)->data(0, Qt::UserRole).value<field_info *>()) {
|
|
|
|
tree_widget_->setCurrentItem((*iter));
|
2015-02-26 16:18:04 +00:00
|
|
|
tree_widget_->scrollToItem((*iter));
|
2014-10-24 20:24:23 +00:00
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
iter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-10-27 17:28:45 +00:00
|
|
|
void ByteViewText::contextMenuEvent(QContextMenuEvent *event)
|
|
|
|
{
|
|
|
|
ctx_menu_.exec(event->globalPos());
|
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Private
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
const int ByteViewText::separator_interval_ = 8; // Insert a space after this many bytes
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-25 19:36:32 +00:00
|
|
|
// Draw a line of byte view text for a given offset.
|
|
|
|
// Text with different styles are split into fragments and passed to
|
|
|
|
// flushOffsetFragment. Font character widths aren't necessarily whole
|
|
|
|
// numbers so we track our X coordinate position using using floats.
|
2014-10-24 20:24:23 +00:00
|
|
|
void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const int row_y)
|
|
|
|
{
|
|
|
|
if (!tvb_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
guint tvb_len = tvb_captured_length(tvb_);
|
|
|
|
guint max_pos = qMin(offset + row_width_, tvb_len);
|
|
|
|
const guint8 *pd = tvb_get_ptr(tvb_, 0, -1);
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
static const guchar hexchars[16] = {
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
QString text;
|
|
|
|
highlight_state state = StateNormal, offset_state = StateOffsetNormal;
|
2014-10-25 19:36:32 +00:00
|
|
|
qreal hex_x = offsetPixels() + margin_;
|
|
|
|
qreal ascii_x = offsetPixels() + hexPixels() + margin_;
|
2014-10-24 20:24:23 +00:00
|
|
|
|
|
|
|
// Hex
|
|
|
|
if (show_hex_) {
|
|
|
|
for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
|
|
|
|
highlight_state hex_state = StateNormal;
|
2014-10-25 19:36:32 +00:00
|
|
|
bool add_space = tvb_pos != offset;
|
2014-10-24 20:24:23 +00:00
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
|
2014-10-24 20:24:23 +00:00
|
|
|
hex_state = StateField;
|
|
|
|
offset_state = StateOffsetField;
|
2014-10-27 01:48:14 +00:00
|
|
|
} else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
|
2014-10-24 20:24:23 +00:00
|
|
|
hex_state = StateProtocol;
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (hex_state != state) {
|
2014-10-25 19:36:32 +00:00
|
|
|
if ((state == StateNormal || (state == StateProtocol && hex_state == StateField)) && add_space) {
|
2014-10-24 20:24:23 +00:00
|
|
|
add_space = false;
|
|
|
|
text += ' ';
|
|
|
|
/* insert a space every separator_interval_ bytes */
|
|
|
|
if ((tvb_pos % separator_interval_) == 0)
|
|
|
|
text += ' ';
|
|
|
|
}
|
|
|
|
hex_x += flushOffsetFragment(painter, hex_x, row_y, state, text);
|
|
|
|
state = hex_state;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (add_space) {
|
|
|
|
text += ' ';
|
|
|
|
/* insert a space every separator_interval_ bytes */
|
|
|
|
if ((tvb_pos % separator_interval_) == 0)
|
|
|
|
text += ' ';
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
switch (format_) {
|
|
|
|
case BYTES_HEX:
|
2014-10-24 20:24:23 +00:00
|
|
|
text += hexchars[(pd[tvb_pos] & 0xf0) >> 4];
|
|
|
|
text += hexchars[pd[tvb_pos] & 0x0f];
|
2012-10-30 19:21:24 +00:00
|
|
|
break;
|
|
|
|
case BYTES_BITS:
|
|
|
|
/* XXX, bitmask */
|
2014-10-24 20:24:23 +00:00
|
|
|
for (int j = 7; j >= 0; j--)
|
|
|
|
text += (pd[tvb_pos] & (1 << j)) ? '1' : '0';
|
2012-10-30 19:21:24 +00:00
|
|
|
break;
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 20:24:23 +00:00
|
|
|
if (text.length() > 0) {
|
|
|
|
flushOffsetFragment(painter, hex_x, row_y, state, text);
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2014-10-24 20:24:23 +00:00
|
|
|
state = StateNormal;
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// ASCII
|
|
|
|
if (show_ascii_) {
|
|
|
|
for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
|
|
|
|
highlight_state ascii_state = StateNormal;
|
2014-10-25 19:36:32 +00:00
|
|
|
bool add_space = tvb_pos != offset;
|
2014-10-24 20:24:23 +00:00
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
|
2014-10-24 20:24:23 +00:00
|
|
|
ascii_state = StateField;
|
|
|
|
offset_state = StateOffsetField;
|
2014-10-27 01:48:14 +00:00
|
|
|
} else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
|
2014-10-24 20:24:23 +00:00
|
|
|
ascii_state = StateProtocol;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (ascii_state != state) {
|
2014-10-25 19:36:32 +00:00
|
|
|
if ((state == StateNormal || (state == StateProtocol && ascii_state == StateField)) && add_space) {
|
2014-10-24 20:24:23 +00:00
|
|
|
add_space = false;
|
|
|
|
/* insert a space every separator_interval_ bytes */
|
|
|
|
if ((tvb_pos % separator_interval_) == 0)
|
|
|
|
text += ' ';
|
|
|
|
}
|
|
|
|
ascii_x += flushOffsetFragment(painter, ascii_x, row_y, state, text);
|
|
|
|
state = ascii_state;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
if (add_space) {
|
|
|
|
/* insert a space every separator_interval_ bytes */
|
|
|
|
if ((tvb_pos % separator_interval_) == 0)
|
|
|
|
text += ' ';
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
guchar c = (encoding_ == PACKET_CHAR_ENC_CHAR_EBCDIC) ?
|
|
|
|
EBCDIC_to_ASCII1(pd[tvb_pos]) :
|
|
|
|
pd[tvb_pos];
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
text += g_ascii_isprint(c) ? c : '.';
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-24 20:24:23 +00:00
|
|
|
if (text.length() > 0) {
|
|
|
|
flushOffsetFragment(painter, ascii_x, row_y, state, text);
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2014-10-24 20:24:23 +00:00
|
|
|
state = StateNormal;
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Offset. Must be drawn last in order for offset_state to be set.
|
|
|
|
if (show_offset_) {
|
|
|
|
text = QString("%1").arg(offset, offsetChars(), 16, QChar('0'));
|
|
|
|
flushOffsetFragment(painter, margin_, row_y, offset_state, text);
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Draws a fragment of byte view text at the specifiec location using colors
|
|
|
|
// for the specified state. Clears the text and returns the pixel width of the
|
|
|
|
// drawn text.
|
2014-10-25 19:36:32 +00:00
|
|
|
qreal ByteViewText::flushOffsetFragment(QPainter &painter, qreal x, int y, highlight_state state, QString &text)
|
2012-01-04 22:13:01 +00:00
|
|
|
{
|
2014-10-24 20:24:23 +00:00
|
|
|
if (text.length() < 1) {
|
|
|
|
return 0;
|
|
|
|
}
|
2014-10-25 19:36:32 +00:00
|
|
|
QFontMetricsF fm(mono_font_);
|
|
|
|
qreal width = fm.width(text);
|
2014-10-24 20:24:23 +00:00
|
|
|
// Background
|
|
|
|
if (state == StateField) {
|
2014-10-25 19:36:32 +00:00
|
|
|
painter.fillRect(QRectF(x, y, width, line_spacing_), palette().highlight());
|
2014-10-24 20:24:23 +00:00
|
|
|
} else if (state == StateProtocol) {
|
2014-10-29 00:10:45 +00:00
|
|
|
painter.fillRect(QRectF(x, y, width, line_spacing_), palette().window());
|
2014-10-24 20:24:23 +00:00
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Text
|
|
|
|
QBrush text_brush;
|
2012-10-30 19:21:24 +00:00
|
|
|
switch (state) {
|
2014-10-24 20:24:23 +00:00
|
|
|
case StateNormal:
|
2014-10-26 00:19:38 +00:00
|
|
|
case StateProtocol:
|
2014-10-24 20:24:23 +00:00
|
|
|
default:
|
2014-10-29 00:10:45 +00:00
|
|
|
text_brush = palette().windowText();
|
2012-10-30 19:21:24 +00:00
|
|
|
break;
|
|
|
|
case StateField:
|
2014-10-24 20:24:23 +00:00
|
|
|
text_brush = palette().highlightedText();
|
2012-10-30 19:21:24 +00:00
|
|
|
break;
|
2014-10-24 20:24:23 +00:00
|
|
|
case StateOffsetNormal:
|
2014-10-25 19:36:32 +00:00
|
|
|
text_brush = offset_normal_fg_;
|
2014-10-24 20:24:23 +00:00
|
|
|
break;
|
|
|
|
case StateOffsetField:
|
2014-10-25 19:36:32 +00:00
|
|
|
text_brush = offset_field_fg_;
|
2012-10-30 19:21:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
painter.setPen(QPen(text_brush.color()));
|
2014-10-25 19:36:32 +00:00
|
|
|
painter.drawText(QRectF(x, y, width, line_spacing_), Qt::AlignTop, text);
|
2014-10-24 20:24:23 +00:00
|
|
|
text.clear();
|
|
|
|
return width;
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-10-30 19:21:24 +00:00
|
|
|
void ByteViewText::scrollToByte(int byte)
|
2012-01-04 22:13:01 +00:00
|
|
|
{
|
2014-10-24 20:24:23 +00:00
|
|
|
verticalScrollBar()->setValue(byte / row_width_);
|
2012-10-30 19:21:24 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Offset character width
|
|
|
|
int ByteViewText::offsetChars()
|
2012-10-30 19:21:24 +00:00
|
|
|
{
|
2014-10-24 20:24:23 +00:00
|
|
|
if (tvb_ && tvb_captured_length(tvb_) > 0xffff) {
|
|
|
|
return 8;
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2014-10-24 20:24:23 +00:00
|
|
|
return 4;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Offset pixel width
|
|
|
|
int ByteViewText::offsetPixels()
|
|
|
|
{
|
|
|
|
if (show_offset_) {
|
|
|
|
return offsetChars() * font_width_ + one_em_;
|
|
|
|
}
|
|
|
|
return 0;
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
// Hex pixel width
|
|
|
|
int ByteViewText::hexPixels()
|
|
|
|
{
|
|
|
|
if (show_hex_) {
|
2014-10-27 17:28:45 +00:00
|
|
|
int digits_per_byte = format_ == BYTES_HEX ? 3 : 9;
|
|
|
|
return (((row_width_ * digits_per_byte) + ((row_width_ - 1) / separator_interval_)) * font_width_) + one_em_;
|
2014-10-24 20:24:23 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
int ByteViewText::asciiPixels()
|
|
|
|
{
|
|
|
|
if (show_ascii_) {
|
|
|
|
return ((row_width_ + ((row_width_ - 1) / separator_interval_)) * font_width_) + one_em_;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
int ByteViewText::totalPixels()
|
|
|
|
{
|
|
|
|
return offsetPixels() + hexPixels() + asciiPixels();
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2014-10-24 20:24:23 +00:00
|
|
|
void ByteViewText::updateScrollbars()
|
|
|
|
{
|
|
|
|
const gint length = tvb_ ? tvb_captured_length(tvb_) : 0;
|
|
|
|
if (tvb_) {
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 19:36:32 +00:00
|
|
|
qint64 maxval = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / line_spacing_;
|
2014-10-24 20:24:23 +00:00
|
|
|
|
|
|
|
verticalScrollBar()->setRange(0, qMax((qint64)0, maxval));
|
|
|
|
horizontalScrollBar()->setRange(0, qMax(0, static_cast<int>((totalPixels() - viewport()->width()) / font_width_)));
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2012-10-30 19:21:24 +00:00
|
|
|
|
2014-10-27 01:48:14 +00:00
|
|
|
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_);
|
|
|
|
}
|
|
|
|
|
2014-10-27 17:28:45 +00:00
|
|
|
void ByteViewText::setHexDisplayFormat(QAction *action)
|
|
|
|
{
|
|
|
|
if (!action) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
format_ = action->data().value<bytes_view_type>();
|
|
|
|
row_width_ = format_ == BYTES_HEX ? 16 : 8;
|
|
|
|
viewport()->update();
|
|
|
|
}
|
|
|
|
|
2012-09-04 08:18:31 +00:00
|
|
|
/*
|
|
|
|
* Editor modelines
|
|
|
|
*
|
|
|
|
* Local Variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* ex: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|