aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2016-11-25 12:50:46 -0600
committerRoland Knall <rknall@gmail.com>2017-12-07 19:00:35 +0000
commitbe8a40005328d74815491b3536b85fe9e532243e (patch)
treec74b76c0f462d6772be046cb6f07d669acbb045a
parentf8b19c6eecbf241ecede5b85811a772469cea0df (diff)
Qt: Use QTextLayout in ByteViewText.
Use QTextLayout to draw each line in ByteViewText instead of drawing fragments ourselves. Build our pixel-to-byte-offset map when we draw our first line, which should hopefully make it more accurate. This should fix layout and hover issues on some systems. Start moving common code to DataPrinter. Mark prefs.gui_hex_dump_highlight_style GTK+ only. Bug: 11844 Change-Id: Ifda16ae7dc1a5ea22570c0bfd0eb20cee621bfc9 Reviewed-on: https://code.wireshark.org/review/24717 Reviewed-by: Gerald Combs <gerald@wireshark.org> Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall <rknall@gmail.com>
-rw-r--r--epan/prefs.c17
-rw-r--r--ui/qt/byte_view_tab.cpp13
-rw-r--r--ui/qt/utils/data_printer.cpp23
-rw-r--r--ui/qt/utils/data_printer.h18
-rw-r--r--ui/qt/utils/field_information.cpp51
-rw-r--r--ui/qt/utils/field_information.h15
-rw-r--r--ui/qt/widgets/byte_view_text.cpp404
-rw-r--r--ui/qt/widgets/byte_view_text.h70
8 files changed, 261 insertions, 350 deletions
diff --git a/epan/prefs.c b/epan/prefs.c
index 89a4e54f50..9ae9cfd21b 100644
--- a/epan/prefs.c
+++ b/epan/prefs.c
@@ -5,19 +5,7 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include "config.h"
@@ -109,6 +97,7 @@ static const enum_val_t gui_ptree_expander_style[] = {
{NULL, NULL, -1}
};
+/* GTK+ only. */
static const enum_val_t gui_hex_dump_highlight_style[] = {
{"BOLD", "BOLD", 0},
{"INVERSE", "INVERSE", 1},
@@ -3959,7 +3948,7 @@ pre_init_prefs(void)
prefs.gui_expert_composite_eyecandy = FALSE;
prefs.gui_ptree_line_style = 0;
prefs.gui_ptree_expander_style = 1;
- prefs.gui_hex_dump_highlight_style = 1;
+ prefs.gui_hex_dump_highlight_style = 1; /* GTK+ only */
prefs.filter_toolbar_show_in_statusbar = FALSE;
prefs.restore_filter_after_following_stream = FALSE;
prefs.gui_toolbar_main_style = TB_STYLE_ICONS;
diff --git a/ui/qt/byte_view_tab.cpp b/ui/qt/byte_view_tab.cpp
index a038f637df..7bf47921c1 100644
--- a/ui/qt/byte_view_tab.cpp
+++ b/ui/qt/byte_view_tab.cpp
@@ -25,7 +25,6 @@
#include <QClipboard>
#include <QMimeData>
#include <QTabBar>
-#include <QTreeWidgetItem>
#include "cfile.h"
#include "epan/epan_dissect.h"
@@ -266,23 +265,23 @@ void ByteViewTab::selectedFieldChanged(FieldInformation *selected)
if (byte_view_text)
{
- int f_start = -1, f_end = -1;
+ int f_start = -1, f_length = -1;
if (cap_file_->search_in_progress && (cap_file_->hex || (cap_file_->string && cap_file_->packet_data))) {
// In the hex view, only highlight the target bytes or string. The entire
// field can then be displayed by clicking on any of the bytes in the field.
f_start = cap_file_->search_pos - cap_file_->search_len + 1;
- f_end = f_start + cap_file_->search_len;
+ f_length = (int) cap_file_->search_len;
} else {
f_start = selected->position().start;
- f_end = selected->position().end;
+ f_length = selected->position().length;
}
setCurrentIndex(idx);
- byte_view_text->markField(f_start, f_end);
- byte_view_text->markProtocol(selected->parentField()->position().start, selected->parentField()->position().end);
- byte_view_text->markAppendix(selected->appendix().start, selected->appendix().end);
+ byte_view_text->markField(f_start, f_length);
+ byte_view_text->markProtocol(selected->parentField()->position().start, selected->parentField()->position().length);
+ byte_view_text->markAppendix(selected->appendix().start, selected->appendix().length);
}
}
diff --git a/ui/qt/utils/data_printer.cpp b/ui/qt/utils/data_printer.cpp
index fbb8a3c797..9d2d7740fb 100644
--- a/ui/qt/utils/data_printer.cpp
+++ b/ui/qt/utils/data_printer.cpp
@@ -4,21 +4,11 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
+#include "ui/recent.h"
+
#include <ui/qt/utils/data_printer.h>
#include <stdint.h>
@@ -113,6 +103,13 @@ int DataPrinter::byteLineLength() const
return byteLineLength_;
}
+int DataPrinter::hexChars()
+{
+ int row_width = recent.gui_bytes_view == BYTES_HEX ? 16 : 8;
+ int chars_per_byte = recent.gui_bytes_view == BYTES_HEX ? 3 : 9;
+ return (row_width * chars_per_byte) + ((row_width - 1) / separatorInterval());
+}
+
QString DataPrinter::hexTextDump(QByteArray printData, bool showText)
{
QString clipboard_text;
diff --git a/ui/qt/utils/data_printer.h b/ui/qt/utils/data_printer.h
index 923c15169b..42a5f0c04d 100644
--- a/ui/qt/utils/data_printer.h
+++ b/ui/qt/utils/data_printer.h
@@ -7,19 +7,7 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef DATA_PRINTER_H
@@ -57,6 +45,10 @@ public:
void setByteLineLength(int);
int byteLineLength() const;
+ // Insert a space after this many bytes
+ static int separatorInterval() { return 8; }
+ // The number of hexadecimal characters per line
+ static int hexChars();
private:
QString hexTextDump(QByteArray printData, bool append_text);
diff --git a/ui/qt/utils/field_information.cpp b/ui/qt/utils/field_information.cpp
index fc58222101..aac03277c5 100644
--- a/ui/qt/utils/field_information.cpp
+++ b/ui/qt/utils/field_information.cpp
@@ -4,19 +4,7 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <stdint.h>
@@ -87,29 +75,22 @@ bool FieldInformation::tvbContains(FieldInformation *child)
FieldInformation::Position FieldInformation::position() const
{
- Position pos = {-1, -1, -1};
+ Position pos = {-1, -1};
if ( fi_ && fi_->ds_tvb )
{
- guint len = tvb_captured_length(fi_->ds_tvb);
+ int len = (int) tvb_captured_length(fi_->ds_tvb);
pos.start = fi_->start;
pos.length = fi_->length;
- if (pos.start >= 0 && pos.length > 0 && (guint)pos.start < len)
- {
- pos.end = pos.start + pos.length;
- }
- else
+ if (pos.start < 0 || pos.length < 0 || pos.start >= len)
{
- if ( fi_->appendix_start >= 0 && fi_->appendix_length > 0 && (guint)fi_->appendix_start < len )
+ if ( fi_->appendix_start >= 0 && fi_->appendix_length > 0 && fi_->appendix_start < len )
{
pos.start = fi_->appendix_start;
- pos.end = fi_->appendix_start + fi_->appendix_length;
+ pos.length = fi_->appendix_length;
}
}
-
- if (pos.end != -1 && (guint)pos.end > len)
- pos.end = len;
}
return pos;
@@ -117,31 +98,16 @@ FieldInformation::Position FieldInformation::position() const
FieldInformation::Position FieldInformation::appendix() const
{
- Position pos = {-1, -1, -1};
+ Position pos = {-1, -1};
if ( fi_ && fi_->ds_tvb )
{
- guint len = tvb_captured_length(fi_->ds_tvb);
-
pos.start = fi_->appendix_start;
pos.length = fi_->appendix_length;
-
- if (pos.start >= 0 && pos.length > 0 && (guint)pos.start < len)
- pos.end = pos.start + pos.length;
-
- /* sanity check with total field length */
- if ( position().end == -1 )
- {
- pos.start = -1;
- pos.end = -1;
- }
-
- if (pos.end != -1 && (guint)pos.end > len)
- pos.end = len;
}
return pos;
}
-#include <QDebug>
+
QByteArray FieldInformation::printableData()
{
QByteArray data;
@@ -154,7 +120,6 @@ QByteArray FieldInformation::printableData()
int length = pos.length;
if ( length > rem_length )
length = rem_length;
-qDebug() << "Bin hier";
uint8_t * dataSet = (uint8_t *)tvb_memdup(wmem_file_scope(), fi_->ds_tvb, pos.start, length );
data = QByteArray::fromRawData((char *)dataSet, length);
}
diff --git a/ui/qt/utils/field_information.h b/ui/qt/utils/field_information.h
index 9f24f18f56..19eacd3ca5 100644
--- a/ui/qt/utils/field_information.h
+++ b/ui/qt/utils/field_information.h
@@ -4,19 +4,7 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef FIELD_INFORMATION_H_
@@ -48,7 +36,6 @@ public:
struct Position
{
int start;
- int end;
int length;
};
diff --git a/ui/qt/widgets/byte_view_text.cpp b/ui/qt/widgets/byte_view_text.cpp
index 93612e514b..c66af61761 100644
--- a/ui/qt/widgets/byte_view_text.cpp
+++ b/ui/qt/widgets/byte_view_text.cpp
@@ -4,24 +4,9 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
-// Some code based on QHexView by Even Teran
-// https://code.google.com/p/qhexview/
-
#include "byte_view_text.h"
#include <epan/charsets.h>
@@ -32,6 +17,7 @@
#include "wireshark_application.h"
#include "ui/recent.h"
+#include <ui/qt/utils/data_printer.h>
#include <ui/qt/utils/variant_pointer.h>
#include <QActionGroup>
@@ -40,39 +26,43 @@
#include <QScrollBar>
#include <QStyle>
#include <QStyleOption>
+#include <QTextLayout>
// To do:
// - Add recent settings and context menu items to show/hide the offset,
// and ASCII/EBCDIC.
// - Add a UTF-8 and possibly UTF-xx option to the ASCII display.
// - Add "copy bytes as" context menu items.
-
-// 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.
+// - Add back hover.
+// - Move more common metrics to DataPrinter.
Q_DECLARE_METATYPE(bytes_view_type)
Q_DECLARE_METATYPE(packet_char_enc)
ByteViewText::ByteViewText(QByteArray data, packet_char_enc encoding, QWidget *parent) :
QAbstractScrollArea(parent),
- bold_highlight_(false),
+ layout_(new QTextLayout()),
encoding_(encoding),
hovered_byte_offset_(-1),
hovered_byte_lock_(false),
- p_bound_(0, 0),
- f_bound_(0, 0),
- fa_bound_(0, 0),
+ proto_start_(0),
+ proto_len_(0),
+ field_start_(0),
+ field_len_(0),
+ field_a_start_(0),
+ field_a_len_(0),
show_offset_(true),
show_hex_(true),
show_ascii_(true),
row_width_(recent.gui_bytes_view == BYTES_HEX ? 16 : 8),
- one_em_(0),
font_width_(0),
- line_spacing_(0),
- margin_(0)
+ line_height_(0)
{
data_ = data;
+ layout_->setCacheEnabled(true);
+
+ offset_normal_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35);
+ offset_field_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65);
createContextMenu();
@@ -86,6 +76,7 @@ ByteViewText::ByteViewText(QByteArray data, packet_char_enc encoding, QWidget *p
ByteViewText::~ByteViewText()
{
ctx_menu_.clear();
+ delete(layout_);
}
void ByteViewText::createContextMenu()
@@ -139,11 +130,6 @@ QByteArray ByteViewText::viewData()
return data_;
}
-void ByteViewText::setHighlightStyle(bool bold)
-{
- bold_highlight_ = bold;
-}
-
bool ByteViewText::isEmpty() const
{
return data_.isEmpty();
@@ -151,22 +137,22 @@ bool ByteViewText::isEmpty() const
QSize ByteViewText::minimumSizeHint() const
{
- // Allow panel to be shrinked to any size
+ // Allow panel to shrink to any size
return QSize();
}
-void ByteViewText::markProtocol(int start, int end)
+void ByteViewText::markProtocol(int start, int length)
{
- p_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
- p_bound_save_ = p_bound_;
+ proto_start_ = start;
+ proto_len_ = length;
viewport()->update();
}
-void ByteViewText::markField(int start, int end)
+void ByteViewText::markField(int start, int length)
{
- f_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
+ field_start_ = start;
+ field_len_ = length;
scrollToByte(start);
- f_bound_save_ = f_bound_;
viewport()->update();
}
@@ -177,10 +163,10 @@ void ByteViewText::moveToOffset(int pos)
}
-void ByteViewText::markAppendix(int start, int end)
+void ByteViewText::markAppendix(int start, int length)
{
- fa_bound_ = QPair<guint, guint>(qMax(0, start), qMax(0, end));
- fa_bound_save_ = f_bound_;
+ field_a_start_ = start;
+ field_a_len_ = length;
viewport()->update();
}
@@ -190,11 +176,12 @@ void ByteViewText::setMonospaceFont(const QFont &mono_font)
const QFontMetricsF fm(mono_font);
font_width_ = fm.width('M');
- line_spacing_ = fm.lineSpacing() + 0.5;
- one_em_ = fm.height();
- margin_ = fm.height() / 2;
setFont(mono_font);
+ layout_->setFont(mono_font);
+
+ // We should probably use ProtoTree::rowHeight.
+ line_height_ = fontMetrics().height();
updateScrollbars();
viewport()->update();
@@ -210,14 +197,12 @@ void ByteViewText::paintEvent(QPaintEvent *)
int row_y = 0;
// Starting byte offset
- guint offset = (guint) verticalScrollBar()->value() * row_width_;
+ int offset = verticalScrollBar()->value() * row_width_;
// Clear the area
painter.fillRect(viewport()->rect(), palette().base());
- // Offset background
- offset_normal_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35));
- offset_field_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65));
+ // Offset background. We want the entire height to be filled.
if (show_offset_) {
QRect offset_rect = QRect(viewport()->rect());
offset_rect.setWidth(offsetPixels());
@@ -228,34 +213,17 @@ void ByteViewText::paintEvent(QPaintEvent *)
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_) {
- // Hittable pixels extend 1/2 space on either side of the hex digits
- int pixels_per_byte = (recent.gui_bytes_view == 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++) {
- 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;
- }
- }
- }
-
// Data rows
int widget_height = height();
painter.save();
- while( (int) (row_y + line_spacing_) < widget_height && (int) offset < (int) data_.count()) {
- drawOffsetLine(painter, offset, row_y);
+
+ x_pos_to_column_.clear();
+ while( (int) (row_y + line_height_) < widget_height && offset < (int) data_.count()) {
+ drawLine(&painter, offset, row_y);
offset += row_width_;
- row_y += line_spacing_;
+ row_y += line_height_;
}
+
painter.restore();
QStyleOptionFocusRect option;
@@ -304,187 +272,206 @@ void ByteViewText::contextMenuEvent(QContextMenuEvent *event)
// Private
-const int ByteViewText::separator_interval_ = 8; // Insert a space after this many bytes
+const int ByteViewText::separator_interval_ = DataPrinter::separatorInterval();
// 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.
-void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const int row_y)
+// Text highlighting is handled using QTextLayout::FormatRange.
+void ByteViewText::drawLine(QPainter *painter, const int offset, const int row_y)
{
if (isEmpty()) {
return;
}
- guint tvb_len = data_.count();
- guint max_pos = qMin(offset + row_width_, tvb_len);
+
+ // Build our pixel to byte offset vector the first time through.
+ bool build_x_pos = x_pos_to_column_.empty() ? true : false;
+ int tvb_len = data_.count();
+ int max_tvb_pos = qMin(offset + row_width_, tvb_len) - 1;
+ QList<QTextLayout::FormatRange> fmt_list;
static const guchar hexchars[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- QString text;
- HighlightMode hl_mode = ModeNormal, offset_mode = ModeOffsetNormal;
- qreal hex_x = offsetPixels() + margin_;
- qreal ascii_x = offsetPixels() + hexPixels() + margin_;
+ QString line;
+ HighlightMode offset_mode = ModeOffsetNormal;
+
+ // Offset.
+ if (show_offset_) {
+ line = QString(" %1 ").arg(offset, offsetChars(false), 16, QChar('0'));
+ if (build_x_pos) {
+ x_pos_to_column_.fill(-1, fontMetrics().width(line));
+ }
+ }
// Hex
if (show_hex_) {
- for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
- HighlightMode hex_state = ModeNormal;
- bool add_space = tvb_pos != offset;
- bool draw_hover = tvb_pos == hovered_byte_offset_;
-
- if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
- hex_state = ModeField;
- offset_mode = ModeOffsetField;
- } else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
- hex_state = ModeProtocol;
- }
+ int ascii_start = line.length() + DataPrinter::hexChars() + 3;
+ // Extra hover space before and after each byte.
+ int slop = font_width_ / 2;
- if (hex_state != hl_mode || draw_hover) {
- if ((hl_mode == ModeNormal || (hl_mode == ModeProtocol && hex_state == ModeField) || draw_hover) && add_space) {
- 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, hl_mode, text);
- hl_mode = hex_state;
- }
+ if (build_x_pos) {
+ x_pos_to_column_.append(QVector<int>().fill(-1, slop));
+ }
- if (add_space) {
- text += ' ';
- /* insert a space every separator_interval_ bytes */
- if ((tvb_pos % separator_interval_) == 0)
- text += ' ';
+ for (int tvb_pos = offset; tvb_pos <= max_tvb_pos; tvb_pos++) {
+ line += ' ';
+ /* insert a space every separator_interval_ bytes */
+ if ((tvb_pos != offset) && ((tvb_pos % separator_interval_) == 0)) {
+ line += ' ';
+ x_pos_to_column_.append(QVector<int>().fill(tvb_pos - offset - 1, font_width_));
}
switch (recent.gui_bytes_view) {
case BYTES_HEX:
- text += hexchars[(data_[tvb_pos] & 0xf0) >> 4];
- text += hexchars[data_[tvb_pos] & 0x0f];
+ line += hexchars[(data_[tvb_pos] & 0xf0) >> 4];
+ line += hexchars[data_[tvb_pos] & 0x0f];
break;
case BYTES_BITS:
/* XXX, bitmask */
- for (int j = 7; j >= 0; j--)
- text += (data_[tvb_pos] & (1 << j)) ? '1' : '0';
+ for (int j = 7; j >= 0; j--) {
+ line += (data_[tvb_pos] & (1 << j)) ? '1' : '0';
+ }
break;
}
- if (draw_hover) {
- hex_x += flushOffsetFragment(painter, hex_x, row_y, ModeHover, text);
+ if (build_x_pos) {
+ x_pos_to_column_.append(QVector<int>().fill(tvb_pos - offset, fontMetrics().width(line) - x_pos_to_column_.size() + slop));
}
}
+ line += QString(ascii_start - line.length(), ' ');
+ if (build_x_pos) {
+ x_pos_to_column_.append(QVector<int>().fill(-1, fontMetrics().width(line) - x_pos_to_column_.size()));
+ }
+
+ addHexFormatRange(fmt_list, proto_start_, proto_len_, offset, max_tvb_pos, ModeProtocol);
+ if (addHexFormatRange(fmt_list, field_start_, field_len_, offset, max_tvb_pos, ModeField)) {
+ offset_mode = ModeOffsetField;
+ }
+ addHexFormatRange(fmt_list, field_a_start_, field_a_len_, offset, max_tvb_pos, ModeField);
+ if (hovered_byte_offset_ >= offset && hovered_byte_offset_ <= max_tvb_pos) {
+ addHexFormatRange(fmt_list, hovered_byte_offset_, hovered_byte_offset_ + 1, offset, max_tvb_pos, ModeField);
+ }
}
- if (text.length() > 0) {
- flushOffsetFragment(painter, hex_x, row_y, hl_mode, text);
- }
- hl_mode = ModeNormal;
// ASCII
if (show_ascii_) {
- for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
- HighlightMode ascii_state = ModeNormal;
- bool add_space = tvb_pos != offset;
- bool highlight_text = tvb_pos == hovered_byte_offset_;
-
- if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) {
- ascii_state = ModeField;
- offset_mode = ModeOffsetField;
- } else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) {
- ascii_state = ModeProtocol;
- }
-
- if (ascii_state != hl_mode || highlight_text) {
- if ((hl_mode == ModeNormal || (hl_mode == ModeProtocol && ascii_state == ModeField) || highlight_text) && add_space) {
- add_space = false;
- /* insert a space every separator_interval_ bytes */
- if ((tvb_pos % separator_interval_) == 0)
- text += ' ';
+ for (int tvb_pos = offset; tvb_pos <= max_tvb_pos; tvb_pos++) {
+ /* insert a space every separator_interval_ bytes */
+ if ((tvb_pos != offset) && ((tvb_pos % separator_interval_) == 0)) {
+ line += ' ';
+ if (build_x_pos) {
+ x_pos_to_column_.append(QVector<int>().fill(tvb_pos - offset - 1, font_width_ / 2));
}
- ascii_x += flushOffsetFragment(painter, ascii_x, row_y, hl_mode, text);
- hl_mode = ascii_state;
- }
-
- if (add_space) {
- /* insert a space every separator_interval_ bytes */
- if ((tvb_pos % separator_interval_) == 0)
- text += ' ';
}
guchar c = (encoding_ == PACKET_CHAR_ENC_CHAR_EBCDIC) ?
EBCDIC_to_ASCII1(data_[tvb_pos]) :
data_[tvb_pos];
- text += g_ascii_isprint(c) ? c : '.';
- if (highlight_text) {
- ascii_x += flushOffsetFragment(painter, ascii_x, row_y, ModeHover, text);
+ if (g_ascii_isprint(c)) {
+ line += c;
+ } else {
+ line += UTF8_MIDDLE_DOT;
+ }
+ if (build_x_pos) {
+ x_pos_to_column_.append(QVector<int>().fill(tvb_pos - offset, fontMetrics().width(line) - x_pos_to_column_.size()));
}
}
- }
- if (text.length() > 0) {
- flushOffsetFragment(painter, ascii_x, row_y, hl_mode, text);
+ addAsciiFormatRange(fmt_list, proto_start_, proto_len_, offset, max_tvb_pos, ModeProtocol);
+ if (addAsciiFormatRange(fmt_list, field_start_, field_len_, offset, max_tvb_pos, ModeField)) {
+ offset_mode = ModeOffsetField;
+ }
+ addAsciiFormatRange(fmt_list, field_a_start_, field_a_len_, offset, max_tvb_pos, ModeField);
+ if (hovered_byte_offset_ >= offset && hovered_byte_offset_ <= max_tvb_pos) {
+ addAsciiFormatRange(fmt_list, hovered_byte_offset_, hovered_byte_offset_ + 1, offset, max_tvb_pos, ModeField);
+ }
}
- // 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_mode, text);
- }
+ // XXX Fields won't be highlighted if neither hex nor ascii are enabled.
+ addFormatRange(fmt_list, 0, offsetChars(), offset_mode);
+
+ layout_->clearLayout();
+ layout_->clearAdditionalFormats();
+ layout_->setText(line);
+ layout_->setAdditionalFormats(fmt_list);
+ layout_->beginLayout();
+ QTextLine tl = layout_->createLine();
+ tl.setLineWidth(totalPixels());
+ tl.setPosition(QPointF(0.0, 0.0));
+ layout_->endLayout();
+ layout_->draw(painter, QPointF(0.0, row_y));
}
-// 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.
-qreal ByteViewText::flushOffsetFragment(QPainter &painter, qreal x, int y, HighlightMode mode, QString &text)
+bool ByteViewText::addFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int start, int length, HighlightMode mode)
{
- if (text.length() < 1) {
- return 0;
- }
- QFontMetricsF fm(mono_font_);
- qreal width = fm.width(text);
- QRectF area(x, y, width, line_spacing_);
- // Background
- switch (mode) {
- case ModeField:
- painter.fillRect(area, palette().highlight());
- break;
- case ModeProtocol:
- painter.fillRect(area, palette().window());
- break;
- case ModeHover:
- painter.fillRect(area, ColorUtils::byteViewHoverColor(true));
- break;
- default:
- break;
+ if (length < 1 || mode == ModeNormal) {
+ return false;
}
- // Text
- QBrush text_brush;
+ QTextLayout::FormatRange format_range;
+ format_range.start = start;
+ format_range.length = length;
+ format_range.format.setProperty(QTextFormat::LineHeight, line_height_);
switch (mode) {
case ModeNormal:
- case ModeProtocol:
- default:
- text_brush = palette().windowText();
break;
case ModeField:
- text_brush = palette().highlightedText();
+ format_range.format.setBackground(palette().highlight());
+ break;
+ case ModeProtocol:
+ format_range.format.setBackground(palette().window());
break;
case ModeOffsetNormal:
- text_brush = offset_normal_fg_;
+ format_range.format.setForeground(offset_normal_fg_);
break;
case ModeOffsetField:
- text_brush = offset_field_fg_;
+ format_range.format.setForeground(offset_field_fg_);
break;
case ModeHover:
- text_brush = ColorUtils::byteViewHoverColor(false);
+ format_range.format.setForeground(ColorUtils::byteViewHoverColor(false));
+ format_range.format.setBackground(ColorUtils::byteViewHoverColor(true));
break;
}
+ fmt_list << format_range;
+ return true;
+}
- painter.setPen(QPen(text_brush.color()));
- painter.drawText(area, Qt::AlignTop, text);
- text.clear();
- return width;
+bool ByteViewText::addHexFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, ByteViewText::HighlightMode mode)
+{
+ int mark_end = mark_start + mark_length - 1;
+ if (mark_start < 0 || mark_length < 1) return false;
+ if (mark_start > max_tvb_pos && mark_end < tvb_offset) return false;
+
+ int chars_per_byte = recent.gui_bytes_view == BYTES_HEX ? 3 : 9;
+ int byte_start = qMax(tvb_offset, mark_start) - tvb_offset;
+ int byte_end = qMin(max_tvb_pos, mark_end) - tvb_offset;
+ int fmt_start = offsetChars() + 1 // offset + spacing
+ + (byte_start / separator_interval_)
+ + (byte_start * chars_per_byte);
+ int fmt_length = offsetChars() + 1 // offset + spacing
+ + (byte_end / separator_interval_)
+ + (byte_end * chars_per_byte)
+ + 2 // Both the high and low nibbles.
+ - fmt_start;
+ return addFormatRange(fmt_list, fmt_start, fmt_length, mode);
+}
+
+bool ByteViewText::addAsciiFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, ByteViewText::HighlightMode mode)
+{
+ int mark_end = mark_start + mark_length - 1;
+ if (mark_start < 0 || mark_length < 1) return false;
+ if (mark_start > max_tvb_pos && mark_end < tvb_offset) return false;
+
+ int byte_start = qMax(tvb_offset, mark_start) - tvb_offset;
+ int byte_end = qMin(max_tvb_pos, mark_end) - tvb_offset;
+ int fmt_start = offsetChars() + DataPrinter::hexChars() + 3 // offset + hex + spacing
+ + (byte_start / separator_interval_)
+ + byte_start;
+ int fmt_length = offsetChars() + DataPrinter::hexChars() + 3 // offset + hex + spacing
+ + (byte_end / separator_interval_)
+ + byte_end
+ + 1 // Just one character.
+ - fmt_start;
+ return addFormatRange(fmt_list, fmt_start, fmt_length, mode);
}
void ByteViewText::scrollToByte(int byte)
@@ -493,19 +480,22 @@ void ByteViewText::scrollToByte(int byte)
}
// Offset character width
-int ByteViewText::offsetChars()
+int ByteViewText::offsetChars(bool include_pad)
{
+ int padding = include_pad ? 2 : 0;
if (! isEmpty() && data_.count() > 0xffff) {
- return 8;
+ return 8 + padding;
}
- return 4;
+ return 4 + padding;
}
// Offset pixel width
int ByteViewText::offsetPixels()
{
if (show_offset_) {
- return offsetChars() * font_width_ + one_em_;
+ // One pad space before and after
+ QString zeroes = QString(offsetChars(), '0');
+ return fontMetrics().width(zeroes);
}
return 0;
}
@@ -514,8 +504,9 @@ int ByteViewText::offsetPixels()
int ByteViewText::hexPixels()
{
if (show_hex_) {
- int digits_per_byte = recent.gui_bytes_view == BYTES_HEX ? 3 : 9;
- return (((row_width_ * digits_per_byte) + ((row_width_ - 1) / separator_interval_)) * font_width_) + one_em_;
+ // One pad space before and after
+ QString zeroes = QString(DataPrinter::hexChars() + 2, '0');
+ return fontMetrics().width(zeroes);
}
return 0;
}
@@ -523,7 +514,10 @@ int ByteViewText::hexPixels()
int ByteViewText::asciiPixels()
{
if (show_ascii_) {
- return ((row_width_ + ((row_width_ - 1) / separator_interval_)) * font_width_) + one_em_;
+ // Two pad spaces before, one after
+ int ascii_chars = (row_width_ + ((row_width_ - 1) / separator_interval_));
+ QString zeroes = QString(ascii_chars + 3, '0');
+ return fontMetrics().width(zeroes);
}
return 0;
}
@@ -533,20 +527,22 @@ int ByteViewText::totalPixels()
return offsetPixels() + hexPixels() + asciiPixels();
}
+// We do chunky (per-character) scrolling because it makes some of the
+// math easier. Should we do smooth scrolling?
void ByteViewText::updateScrollbars()
{
const int length = data_.count();
if (length > 0) {
- qint64 maxval = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / line_spacing_;
+ int all_lines_height = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / line_height_;
- verticalScrollBar()->setRange(0, int(qMax((qint64)0, maxval)));
- horizontalScrollBar()->setRange(0, qMax(0, static_cast<int>((totalPixels() - viewport()->width()) / font_width_)));
+ verticalScrollBar()->setRange(0, qMax(0, all_lines_height));
+ horizontalScrollBar()->setRange(0, qMax(0, int((totalPixels() - viewport()->width()) / font_width_)));
}
}
int ByteViewText::byteOffsetAtPixel(QPoint pos)
{
- int byte = (verticalScrollBar()->value() + (pos.y() / line_spacing_)) * row_width_;
+ int byte = (verticalScrollBar()->value() + (pos.y() / line_height_)) * row_width_;
int x = (horizontalScrollBar()->value() * font_width_) + pos.x();
int col = x_pos_to_column_.value(x, -1);
diff --git a/ui/qt/widgets/byte_view_text.h b/ui/qt/widgets/byte_view_text.h
index f5a7430f94..657f4e950a 100644
--- a/ui/qt/widgets/byte_view_text.h
+++ b/ui/qt/widgets/byte_view_text.h
@@ -4,19 +4,7 @@
* 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
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0+
*/
#ifndef BYTE_VIEW_TEXT_H
@@ -27,11 +15,13 @@
#include "ui/recent.h"
#include <QAbstractScrollArea>
-#include <QString>
#include <QFont>
-#include <QSize>
+#include <QVector>
#include <QMenu>
-#include <QMap>
+#include <QSize>
+#include <QString>
+#include <QTextLayout>
+#include <QVector>
// XXX - Is there any reason we shouldn't add ByteViewImage, etc?
@@ -46,13 +36,11 @@ public:
virtual QSize minimumSizeHint() const;
void setFormat(bytes_view_type format);
- void setHighlightStyle(bool bold);
bool isEmpty() const;
QByteArray viewData();
signals:
-
void byteHovered(int pos);
void byteSelected(int pos);
@@ -61,9 +49,9 @@ public slots:
void setMonospaceFont(const QFont &mono_font);
- void markProtocol(int start, int end);
- void markField(int start, int end);
- void markAppendix(int start, int end);
+ void markProtocol(int start, int length);
+ void markField(int start, int length);
+ void markAppendix(int start, int length);
void moveToOffset(int pos);
@@ -86,17 +74,20 @@ private:
ModeHover
} HighlightMode;
+ QTextLayout *layout_;
QByteArray data_;
- void drawOffsetLine(QPainter &painter, const guint offset, const int row_y);
- qreal flushOffsetFragment(QPainter &painter, qreal x, int y, HighlightMode mode, QString &text);
+ void drawLine(QPainter *painter, const int offset, const int row_y);
+ bool addFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int start, int length, HighlightMode mode);
+ bool addHexFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, HighlightMode mode);
+ bool addAsciiFormatRange(QList<QTextLayout::FormatRange> &fmt_list, int mark_start, int mark_length, int tvb_offset, int max_tvb_pos, HighlightMode mode);
void scrollToByte(int byte);
void updateScrollbars();
int byteOffsetAtPixel(QPoint pos);
void createContextMenu();
- int offsetChars();
+ int offsetChars(bool include_pad = true);
int offsetPixels();
int hexPixels();
int asciiPixels();
@@ -106,37 +97,32 @@ private:
// Fonts and colors
QFont mono_font_;
-// QFont mono_bold_font_;
- QBrush offset_normal_fg_;
- QBrush offset_field_fg_;
-
- bool bold_highlight_;
+ QColor offset_normal_fg_;
+ QColor offset_field_fg_;
// Data
packet_char_enc encoding_; // ASCII or EBCDIC
QMenu ctx_menu_;
// Data highlight
- guint hovered_byte_offset_;
+ int hovered_byte_offset_;
bool hovered_byte_lock_;
- 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_;
+ int proto_start_;
+ int proto_len_;
+ int field_start_;
+ int field_len_;
+ int field_a_start_;
+ int field_a_len_;
bool show_offset_; // Should we show the byte offset?
bool show_hex_; // Should we show the hex display?
bool show_ascii_; // Should we show the ASCII display?
- guint row_width_; // Number of bytes per line
- int one_em_; // Font character height
- qreal font_width_; // Font character width
- int line_spacing_; // Font line spacing
- int margin_; // Text margin
+ int row_width_; // Number of bytes per line
+ qreal font_width_; // Single character width and text margin. NOTE: Use fontMetrics::width for multiple characters.
+ int line_height_; // Font line spacing
// Data selection
- QMap<int,int> x_pos_to_column_;
+ QVector<int> x_pos_to_column_;
private slots:
void setHexDisplayFormat(QAction *action);