aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorJakub Zawadzki <darkjames-ws@darkjames.pl>2012-07-15 20:24:48 +0000
committerJakub Zawadzki <darkjames-ws@darkjames.pl>2012-07-15 20:24:48 +0000
commitb69c48df5ae7b42e5d5ef6f1720727f6693efee3 (patch)
treec2f7ceaffc25497f15e1423b40a085c7a4f10bac /ui
parent14ba8d892e5137ba9623e6737cfec44485227df2 (diff)
Fix bug #6690: Wireshark quite slow displaying frames with many detail pane nodes and large byteviews
Add custom widget to render hexdump or bitsdump. svn path=/trunk/; revision=43728
Diffstat (limited to 'ui')
-rw-r--r--ui/gtk/CMakeLists.txt1
-rw-r--r--ui/gtk/Makefile.common2
-rw-r--r--ui/gtk/bytes_view.c1156
-rw-r--r--ui/gtk/bytes_view.h26
-rw-r--r--ui/gtk/old-gtk-compat.h3
-rw-r--r--ui/gtk/packet_panes.c754
6 files changed, 1206 insertions, 736 deletions
diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt
index 48e7d42a33..efc0bb4130 100644
--- a/ui/gtk/CMakeLists.txt
+++ b/ui/gtk/CMakeLists.txt
@@ -26,6 +26,7 @@ set(WIRESHARK_GTK_SRC
about_dlg.c
airpcap_dlg.c
airpcap_gui_utils.c
+ bytes_view.c
capture_dlg.c
capture_file_dlg.c
capture_if_dlg.c
diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common
index ed70d9fdfb..09f906ee16 100644
--- a/ui/gtk/Makefile.common
+++ b/ui/gtk/Makefile.common
@@ -51,6 +51,7 @@ WIRESHARK_GTK_SRC = \
about_dlg.c \
airpcap_dlg.c \
airpcap_gui_utils.c \
+ bytes_view.c \
capture_dlg.c \
capture_file_dlg.c \
capture_if_dlg.c \
@@ -241,6 +242,7 @@ noinst_HEADERS = \
about_dlg.h \
airpcap_dlg.h \
airpcap_gui_utils.h \
+ bytes_view.h \
capture_comment_icons.h \
capture_dlg.h \
capture_file_dlg.h \
diff --git a/ui/gtk/bytes_view.c b/ui/gtk/bytes_view.c
new file mode 100644
index 0000000000..816fac6aa6
--- /dev/null
+++ b/ui/gtk/bytes_view.c
@@ -0,0 +1,1156 @@
+/* bytes_view.c
+ *
+ * $Id$
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Code based on:
+ * xtext, the text widget used by X-Chat. Peter Zelezny <zed@xchat.org>.
+ * GtkTextView. Copyright (C) 2000 Red Hat, Inc.
+ * GtkHex. Jaka Mocnik <jaka@gnu.org>.
+ * pango-layout.c: High-level layout driver. Copyright (C) 2000, 2001, 2006 Red Hat Software
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#undef GTK_DISABLE_DEPRECATED
+#undef GSEAL_ENABLE
+
+#include <gtk/gtk.h>
+#include "ui/gtk/old-gtk-compat.h"
+
+#include <string.h>
+
+#include "../isprint.h"
+
+#include <epan/charsets.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+
+#include "ui/recent.h"
+
+#include "packet_panes.h"
+
+#define MARGIN 2
+#define REFRESH_TIMEOUT 10
+
+static GtkWidgetClass *parent_class = NULL;
+
+struct _BytesView
+{
+ GtkWidget widget;
+
+ PangoContext *context;
+
+ PangoFontDescription *font;
+ int font_ascent;
+ int font_descent;
+ int fontsize;
+
+ gint adj_tag;
+ GtkAdjustment *vadj;
+ GtkAdjustment *hadj;
+ int max_width;
+
+ gboolean bold_highlight;
+ int state;
+
+/* data */
+ int encoding;
+ int format;
+ guint8 *pd;
+ int len;
+/* data-highlight */
+ int start[2];
+ int end[2];
+
+ int per_line;
+ int use_digits;
+};
+
+#include "bytes_view.h"
+
+typedef struct _BytesViewClass
+{
+ GtkWidgetClass parent_class;
+
+ void (*set_scroll_adjustments)(BytesView *, GtkAdjustment *, GtkAdjustment *);
+
+} BytesViewClass;
+
+static void bytes_view_set_scroll_adjustments(BytesView *, GtkAdjustment *, GtkAdjustment *);
+static void bytes_view_adjustment_set(BytesView *);
+
+static void
+bytes_view_init(BytesView *bv)
+{
+ bv->context = NULL;
+
+ bv->encoding = PACKET_CHAR_ENC_CHAR_ASCII;
+ bv->format = BYTES_HEX;
+
+ bv->per_line = 16;
+ bv->use_digits = 4;
+
+ bv->max_width = 0;
+}
+
+static void
+bytes_view_destroy(GtkObject *object)
+{
+ BytesView *bv = BYTES_VIEW(object);
+
+ if (bv->pd) {
+ g_free(bv->pd);
+ bv->pd = NULL;
+ }
+ if (bv->adj_tag) {
+ g_source_remove(bv->adj_tag);
+ bv->adj_tag = 0;
+ }
+ if (bv->vadj) {
+ g_object_unref(G_OBJECT(bv->vadj));
+ bv->vadj = NULL;
+ }
+ if (bv->hadj) {
+ g_object_unref(G_OBJECT(bv->hadj));
+ bv->hadj = NULL;
+ }
+ if (bv->font) {
+ pango_font_description_free(bv->font);
+ bv->font = NULL;
+ }
+ if (bv->context) {
+ g_object_unref(bv->context);
+ bv->context = NULL;
+ }
+ if (GTK_OBJECT_CLASS(parent_class)->destroy)
+ (*GTK_OBJECT_CLASS(parent_class)->destroy)(object);
+}
+
+static void
+bytes_view_ensure_layout(BytesView *bv)
+{
+ if (bv->context == NULL) {
+ bv->context = gtk_widget_get_pango_context(GTK_WIDGET(bv));
+ g_object_ref(bv->context);
+
+ {
+ PangoLanguage *lang;
+ PangoFontMetrics *metrics;
+
+ /* vte and xchat does it this way */
+ lang = pango_context_get_language(bv->context);
+ metrics = pango_context_get_metrics(bv->context, bv->font, lang);
+ bv->font_ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
+ bv->font_descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
+ pango_font_metrics_unref(metrics);
+
+ bv->fontsize = bv->font_ascent + bv->font_descent;
+ }
+ g_assert(bv->context);
+ bytes_view_adjustment_set(bv);
+ }
+}
+
+static void
+bytes_view_realize(GtkWidget *widget)
+{
+ BytesView *bv;
+ GdkWindowAttr attributes;
+
+ _gtk_widget_set_realized_true(widget);
+ bv = BYTES_VIEW(widget);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual(widget);
+ attributes.colormap = gtk_widget_get_colormap(widget);
+
+ attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+
+ widget->window = gdk_window_new(widget->parent->window, &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP);
+
+ gdk_window_set_user_data(widget->window, widget);
+
+ gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
+ widget->style = gtk_style_attach(widget->style, widget->window);
+ bytes_view_ensure_layout(bv);
+}
+
+static void
+bytes_view_unrealize(GtkWidget *widget)
+{
+ BytesView *bv = BYTES_VIEW(widget);
+
+ if (bv->context) {
+ g_object_unref(bv->context);
+ bv->context = NULL;
+ }
+ /* if there are still events in the queue, this'll avoid segfault */
+ gdk_window_set_user_data(widget->window, NULL);
+
+ if (parent_class->unrealize)
+ (*GTK_WIDGET_CLASS(parent_class)->unrealize)(widget);
+}
+
+static GtkAdjustment *
+bytes_view_ensure_vadj(BytesView *bv)
+{
+ if (bv->vadj == NULL) {
+ bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj);
+ g_assert(bv->vadj != NULL);
+ }
+ return bv->vadj;
+}
+
+static GtkAdjustment *
+bytes_view_ensure_hadj(BytesView *bv)
+{
+ if (bv->hadj == NULL) {
+ bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj);
+ g_assert(bv->hadj != NULL);
+ }
+ return bv->hadj;
+}
+
+static gboolean
+bytes_view_scroll(GtkWidget *widget, GdkEventScroll *event)
+{
+ BytesView *bv = BYTES_VIEW(widget);
+
+ gfloat new_value;
+
+ if (event->direction == GDK_SCROLL_UP) { /* mouse wheel pageUp */
+ bytes_view_ensure_vadj(bv);
+
+ new_value = bv->vadj->value - (bv->vadj->page_increment / 10);
+ if (new_value < bv->vadj->lower)
+ new_value = bv->vadj->lower;
+ gtk_adjustment_set_value(bv->vadj, new_value);
+
+ } else if (event->direction == GDK_SCROLL_DOWN) { /* mouse wheel pageDn */
+ bytes_view_ensure_vadj(bv);
+
+ new_value = bv->vadj->value + (bv->vadj->page_increment / 10);
+ if (new_value > (bv->vadj->upper - bv->vadj->page_size))
+ new_value = bv->vadj->upper - bv->vadj->page_size;
+ gtk_adjustment_set_value(bv->vadj, new_value);
+ }
+ return FALSE;
+}
+
+static void
+bytes_view_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ widget->allocation = *allocation;
+ if (gtk_widget_get_realized(widget)) {
+ BytesView *bv = BYTES_VIEW(widget);
+
+ gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
+ bytes_view_adjustment_set(bv);
+ }
+}
+
+static void
+bytes_view_size_request(GtkWidget *widget _U_, GtkRequisition *requisition)
+{
+ requisition->width = 200;
+ requisition->height = 90;
+}
+
+static GSList *
+_pango_runs_build(BytesView *bv, const char *str, int len)
+{
+ GSList *runs = NULL;
+
+ PangoAttrList *attrs;
+ PangoAttrIterator *iter;
+
+ GList *run_list;
+ GList *tmp_list;
+
+ attrs = pango_attr_list_new();
+ pango_attr_list_insert_before(attrs, pango_attr_font_desc_new(bv->font));
+
+ iter = pango_attr_list_get_iterator(attrs);
+
+ run_list = pango_itemize(bv->context, str, 0, len, attrs, iter);
+
+ for (tmp_list = run_list; tmp_list; tmp_list = tmp_list->next) {
+ PangoLayoutRun *run = g_slice_new(PangoLayoutRun);
+ PangoItem *run_item = tmp_list->data;
+
+ run->item = run_item;
+
+ /* XXX pango_layout_get_item_properties(run_item, &state->properties); */
+
+ run->glyphs = pango_glyph_string_new();
+ pango_shape(str + run_item->offset, run_item->length, &run_item->analysis, run->glyphs);
+
+ runs = g_slist_prepend(runs, run);
+ }
+
+ g_list_free(run_list);
+
+ pango_attr_iterator_destroy(iter);
+ pango_attr_list_unref(attrs);
+
+ return g_slist_reverse(runs);
+}
+
+static int
+_pango_glyph_string_to_pixels(PangoGlyphString *glyphs, PangoFont *font _U_)
+{
+#if PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 14
+ return pango_glyph_string_get_width(glyphs) / PANGO_SCALE;
+#else
+ PangoRectangle logical_rect;
+
+ pango_glyph_string_extents(glyphs, font, NULL, &logical_rect);
+ /* pango_extents_to_pixels(&logical_rect, NULL); */
+
+ return (logical_rect.width / PANGO_SCALE);
+#endif
+}
+
+static int
+xtext_draw_layout_line(cairo_t *cr, gint x, gint y, GSList *runs)
+{
+ while (runs) {
+ PangoLayoutRun *run = runs->data;
+
+ cairo_move_to(cr, x, y);
+ pango_cairo_show_glyph_string(cr, run->item->analysis.font, run->glyphs);
+
+ x += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
+ runs = runs->next;
+ }
+ return x;
+}
+
+static int
+_pango_runs_width(GSList *runs)
+{
+ int width = 0;
+
+ while (runs) {
+ PangoLayoutRun *run = runs->data;
+
+ width += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
+ runs = runs->next;
+ }
+ return width;
+}
+
+static void
+_pango_runs_free(GSList *runs)
+{
+ GSList *list = runs;
+
+ while (list) {
+ PangoLayoutRun *run = list->data;
+
+ pango_item_free(run->item);
+ pango_glyph_string_free(run->glyphs);
+ g_slice_free(PangoLayoutRun, run);
+
+ list = list->next;
+ }
+ g_slist_free(runs);
+}
+
+typedef int bytes_view_line_cb(BytesView *, void *data, int x, int arg1, const char *str, int len);
+
+static int
+bytes_view_flush_render(BytesView *bv, void *data, int x, int y, const char *str, int len)
+{
+ cairo_t *cr = data;
+ GSList *line_runs;
+ int str_width;
+
+ if (len < 1)
+ return 0;
+
+ line_runs = _pango_runs_build(bv, str, len);
+
+ /* XXX, cliping */
+
+ if (bv->state != GTK_STATE_NORMAL) {
+ str_width = _pango_runs_width(line_runs);
+
+ /* background */
+ gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->base[bv->state]);
+ cairo_rectangle(cr, x, y - bv->font_ascent, str_width, bv->fontsize);
+ cairo_fill(cr);
+ }
+
+ /* text */
+ gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->text[bv->state]);
+ str_width = xtext_draw_layout_line(cr, x, y, line_runs)-x;
+
+ _pango_runs_free(line_runs);
+
+ return str_width;
+}
+
+static int
+_pango_runs_find_index(GSList *runs, int x_pos, const char *str)
+{
+ int start_pos = 0;
+
+ while (runs) {
+ PangoLayoutRun *run = runs->data;
+ int width;
+
+ width = _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
+
+ if (x_pos >= start_pos && x_pos < start_pos + width) {
+ gboolean char_trailing;
+ int pos;
+
+ pango_glyph_string_x_to_index(run->glyphs,
+ (char *) str + run->item->offset, run->item->length,
+ &run->item->analysis,
+ (x_pos - start_pos) * PANGO_SCALE,
+ &pos, &char_trailing);
+
+ return run->item->offset + pos;
+ }
+
+ start_pos += width;
+ runs = runs->next;
+ }
+ return -1;
+}
+
+static int
+bytes_view_flush_pos(BytesView *bv, void *data, int x, int search_x, const char *str, int len)
+{
+ int *pos_x = data;
+ GSList *line_runs;
+ int line_width;
+
+ if (len < 1)
+ return 0;
+
+ line_runs = _pango_runs_build(bv, str, len);
+
+ line_width = _pango_runs_width(line_runs);
+
+ if (x <= search_x && x + line_width > search_x) {
+ int off_x = search_x - x;
+ int pos_run;
+
+ if ((pos_run = _pango_runs_find_index(line_runs, off_x, str)) != -1)
+ *pos_x = (-*pos_x) + pos_run;
+
+ return -1; /* terminate */
+ } else
+ *pos_x -= len;
+
+ _pango_runs_free(line_runs);
+
+ return line_width;
+}
+
+static void
+bytes_view_render_state(BytesView *bv, int state)
+{
+ g_assert(state == GTK_STATE_NORMAL || state == GTK_STATE_SELECTED);
+
+ if (bv->bold_highlight) {
+ pango_font_description_set_weight(bv->font,
+ (state == GTK_STATE_SELECTED) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+ bv->state = GTK_STATE_NORMAL;
+ } else
+ bv->state = state;
+}
+
+#define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
+
+static void
+_bytes_view_line_common(BytesView *bv, void *data, const int org_off, int xx, int arg1, bytes_view_line_cb flush)
+{
+ static const guchar hexchars[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ const guint8 *pd = bv->pd;
+ const int len = bv->len;
+
+ int state;
+
+ char str[128];
+ int cur = 0;
+
+ int off;
+ guchar c;
+ int byten;
+ int j;
+
+ int scroll_x;
+ int dx;
+
+ g_assert(org_off >= 0);
+
+ scroll_x = bytes_view_ensure_hadj(bv)->value;
+
+ state = GTK_STATE_NORMAL;
+ bytes_view_render_state(bv, GTK_STATE_NORMAL);
+
+ /* Print the line number */
+ j = bv->use_digits;
+ do {
+ j--;
+ c = (org_off >> (j*4)) & 0xF;
+ str[cur++] = hexchars[c];
+ } while (j != 0);
+ str[cur++] = ' ';
+ str[cur++] = ' ';
+
+ /* Print the hex bit */
+ for (byten = 0, off = org_off; byten < bv->per_line; byten++) {
+ gboolean byte_highlighted =
+ (off >= bv->start[0] && off < bv->end[0]) ||
+ (off >= bv->start[1] && off < bv->end[1]);
+ int state_cur = (off < len && byte_highlighted) ?
+ GTK_STATE_SELECTED : GTK_STATE_NORMAL;
+
+ if (state_cur != state) {
+ if (state == GTK_STATE_NORMAL && byten) {
+ str[cur++] = ' ';
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
+ return;
+ xx += dx;
+ cur = 0;
+ bytes_view_render_state(bv, state_cur);
+ state = state_cur;
+
+ if (state == GTK_STATE_NORMAL && byten) {
+ str[cur++] = ' ';
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ } else if (byten) {
+ str[cur++] = ' ';
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ if (off < len) {
+ switch (bv->format) {
+ case BYTES_HEX:
+ str[cur++] = hexchars[(pd[off] & 0xf0) >> 4];
+ str[cur++] = hexchars[pd[off] & 0x0f];
+ break;
+ case BYTES_BITS:
+ /* XXX, bitmask */
+ for (j = 7; j >= 0; j--)
+ str[cur++] = (pd[off] & (1 << j)) ? '1' : '0';
+ break;
+ }
+ } else {
+ switch (bv->format) {
+ case BYTES_HEX:
+ str[cur++] = ' ';
+ str[cur++] = ' ';
+ break;
+ case BYTES_BITS:
+ for (j = 7; j >= 0; j--)
+ str[cur++] = ' ';
+ break;
+ }
+ }
+ off++;
+ }
+
+ if (state != GTK_STATE_NORMAL) {
+ if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
+ return;
+ xx += dx;
+ cur = 0;
+ bytes_view_render_state(bv, GTK_STATE_NORMAL);
+ state = GTK_STATE_NORMAL;
+ }
+
+ /* Print some space at the end of the line */
+ str[cur++] = ' '; str[cur++] = ' '; str[cur++] = ' ';
+
+ /* Print the ASCII bit */
+ for (byten = 0, off = org_off; byten < bv->per_line; byten++) {
+ gboolean byte_highlighted =
+ (off >= bv->start[0] && off < bv->end[0]) ||
+ (off >= bv->start[1] && off < bv->end[1]);
+ int state_cur = (off < len && byte_highlighted) ?
+ GTK_STATE_SELECTED : GTK_STATE_NORMAL;
+
+ if (state_cur != state) {
+ if (state == GTK_STATE_NORMAL && byten) {
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
+ return;
+ xx += dx;
+ cur = 0;
+ bytes_view_render_state(bv, state_cur);
+ state = state_cur;
+
+ if (state == GTK_STATE_NORMAL && byten) {
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ } else if (byten) {
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if ((off % BYTE_VIEW_SEP) == 0)
+ str[cur++] = ' ';
+ }
+
+ if (off < len) {
+ c = (bv->encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) ?
+ EBCDIC_to_ASCII1(pd[off]) :
+ pd[off];
+
+ str[cur++] = isprint(c) ? c : '.';
+ } else
+ str[cur++] = ' ';
+
+ off++;
+ }
+
+ if (cur) {
+ if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
+ return;
+ xx += dx;
+ /* cur = 0; */
+ }
+
+ if (state != GTK_STATE_NORMAL) {
+ bytes_view_render_state(bv, GTK_STATE_NORMAL);
+ /* state = GTK_STATE_NORMAL; */
+ }
+
+ if (bv->max_width < xx)
+ bv->max_width = xx;
+}
+
+static void
+_bytes_view_render_line(BytesView *bv, cairo_t *cr, const int org_off, int yy)
+{
+ _bytes_view_line_common(bv, cr, org_off, MARGIN, yy, bytes_view_flush_render);
+}
+
+static int
+_bytes_view_find_pos(BytesView *bv, const int org_off, int search_x)
+{
+ int pos_x = 0;
+
+ _bytes_view_line_common(bv, &pos_x, org_off, MARGIN, search_x, bytes_view_flush_pos);
+
+ return pos_x;
+}
+
+static void
+bytes_view_render(BytesView *bv, GdkRectangle *area)
+{
+ const int old_max_width = bv->max_width;
+
+ int width, height;
+ GdkDrawable *draw_buf;
+ cairo_t *cr;
+ int y;
+ int off;
+
+ guint line, lines_max;
+ guint lines_max_full;
+
+ if (!gtk_widget_get_realized(GTK_WIDGET(bv)))
+ return;
+
+ bytes_view_ensure_layout(bv);
+
+ draw_buf = GTK_WIDGET(bv)->window;
+ gdk_drawable_get_size(draw_buf, &width, &height);
+
+ if (width < 32 + MARGIN || height < bv->fontsize)
+ return;
+
+ if (area) {
+ line = area->y / bv->fontsize;
+ lines_max = 1 + (area->y + area->height) / bv->fontsize;
+ } else {
+ line = 0;
+ lines_max = (guint) -1;
+ }
+
+ off = 0;
+ y = (bv->fontsize * line);
+
+ cr = gdk_cairo_create(draw_buf);
+ if (area) {
+ gdk_cairo_rectangle(cr, area);
+ /* gdk_cairo_area(cr, rectangle); */
+ cairo_clip(cr);
+ }
+
+ /* clear */
+ gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->base[GTK_STATE_NORMAL]);
+ if (area)
+ cairo_rectangle(cr, area->x, area->y, area->x + area->width, area->y + area->height);
+ else
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_fill(cr);
+
+ if (bv->pd) {
+ guint real_line = line + bytes_view_ensure_vadj(bv)->value;
+
+ lines_max_full = (height / bv->fontsize) + 1;
+ if (lines_max_full < lines_max)
+ lines_max = lines_max_full;
+
+ off = real_line * bv->per_line;
+
+ while (off < bv->len) {
+ _bytes_view_render_line(bv, cr, off, y + bv->font_ascent);
+ line++;
+ if (line >= lines_max)
+ break;
+
+ off += bv->per_line;
+ y += bv->fontsize;
+ }
+ }
+
+ cairo_destroy(cr);
+
+ if (old_max_width != bv->max_width)
+ bytes_view_adjustment_set(bv);
+}
+
+static void
+bytes_view_render_full(BytesView *bv)
+{
+ if (bv->adj_tag) {
+ g_source_remove(bv->adj_tag);
+ bv->adj_tag = 0;
+ }
+ bytes_view_render(bv, NULL);
+}
+
+static void
+bytes_view_paint(GtkWidget *widget, GdkRectangle *area)
+{
+ BytesView *bv = BYTES_VIEW(widget);
+
+ if (area->x == 0 && area->y == 0 && area->height == widget->allocation.height && area->width == widget->allocation.width)
+ bytes_view_render_full(bv);
+ else
+ bytes_view_render(bv, area);
+}
+
+static gboolean
+bytes_view_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ bytes_view_paint(widget, &event->area);
+ return FALSE;
+}
+
+static void
+bytes_view_adjustment_set(BytesView *bv)
+{
+ if (bv->vadj) {
+ bv->vadj->lower = 0;
+ bv->vadj->upper = (int) (bv->len / bv->per_line);
+ if ((bv->len % bv->per_line))
+ bv->vadj->upper++;
+
+ bytes_view_ensure_layout(bv);
+
+ bv->vadj->page_size =
+ (GTK_WIDGET(bv)->allocation.height - bv->font_descent) / bv->fontsize;
+ bv->vadj->step_increment = 1;
+ bv->vadj->page_increment = bv->vadj->page_size;
+
+ if (bv->vadj->value > bv->vadj->upper - bv->vadj->page_size)
+ bv->vadj->value = bv->vadj->upper - bv->vadj->page_size;
+
+ if (bv->vadj->value < 0)
+ bv->vadj->value = 0;
+
+ gtk_adjustment_changed(bv->vadj);
+ }
+
+ if (bv->hadj) {
+ bv->hadj->lower = 0;
+ bv->hadj->upper = bv->max_width;
+
+ bytes_view_ensure_layout(bv);
+
+ bv->hadj->page_size = (GTK_WIDGET(bv)->allocation.width);
+ bv->hadj->step_increment = bv->hadj->page_size / 10.0;
+ bv->hadj->page_increment = bv->hadj->page_size;
+
+ if (bv->hadj->value > bv->hadj->upper - bv->hadj->page_size)
+ bv->hadj->value = bv->hadj->upper - bv->hadj->page_size;
+
+ if (bv->hadj->value < 0)
+ bv->hadj->value = 0;
+
+ gtk_adjustment_changed(bv->hadj);
+ }
+}
+
+static gint
+bytes_view_adjustment_timeout(BytesView *bv)
+{
+ bv->adj_tag = 0;
+ bytes_view_render_full(bv);
+ return 0;
+}
+
+static void
+bytes_view_adjustment_changed(GtkAdjustment *adj, BytesView *bv)
+{
+ /* delay rendering when scrolling (10ms) */
+ if (adj && ((adj == bv->vadj) || (adj == bv->hadj))) {
+ if (!bv->adj_tag)
+ bv->adj_tag = g_timeout_add(REFRESH_TIMEOUT, (GSourceFunc) bytes_view_adjustment_timeout, bv);
+ }
+}
+
+static void
+bytes_view_set_scroll_adjustments(BytesView *bv, GtkAdjustment *hadj, GtkAdjustment *vadj)
+{
+ gboolean need_adjust = FALSE;
+
+ g_return_if_fail(!hadj || GTK_IS_ADJUSTMENT(hadj));
+ g_return_if_fail(!vadj || GTK_IS_ADJUSTMENT(vadj));
+
+ if (!vadj)
+ vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+
+ if (!hadj)
+ hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+
+ if (bv->vadj && (bv->vadj != vadj)) {
+ g_signal_handlers_disconnect_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
+ g_object_unref(bv->vadj);
+ }
+ if (bv->vadj != vadj) {
+ bv->vadj = vadj;
+ g_object_ref_sink(bv->vadj);
+
+ g_signal_connect(bv->vadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv);
+ need_adjust = TRUE;
+ }
+
+ if (bv->hadj && (bv->hadj != hadj)) {
+ g_signal_handlers_disconnect_by_func(bv->hadj, bytes_view_adjustment_changed, bv);
+ g_object_unref(bv->hadj);
+ }
+ if (bv->hadj != hadj) {
+ bv->hadj = hadj;
+ g_object_ref_sink(bv->hadj);
+
+ g_signal_connect(bv->hadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv);
+ need_adjust = TRUE;
+ }
+
+ if (need_adjust)
+ bytes_view_adjustment_set(bv);
+}
+
+static void
+bytes_view_class_init(BytesViewClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ parent_class = (GtkWidgetClass *) g_type_class_peek_parent(klass);
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+
+ object_class->destroy = bytes_view_destroy;
+ widget_class->realize = bytes_view_realize;
+ widget_class->unrealize = bytes_view_unrealize;
+ widget_class->size_request = bytes_view_size_request;
+ widget_class->size_allocate = bytes_view_allocate;
+ widget_class->expose_event = bytes_view_expose;
+ widget_class->scroll_event = bytes_view_scroll;
+
+ klass->set_scroll_adjustments = bytes_view_set_scroll_adjustments;
+
+ widget_class->set_scroll_adjustments_signal =
+ g_signal_new(g_intern_static_string("set-scroll-adjustments"),
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(BytesViewClass, set_scroll_adjustments),
+ NULL, NULL,
+ gtk_marshal_VOID__POINTER_POINTER,
+/* _gtk_marshal_VOID__OBJECT_OBJECT, */
+ G_TYPE_NONE, 2,
+ GTK_TYPE_ADJUSTMENT,
+ GTK_TYPE_ADJUSTMENT);
+}
+
+GType
+bytes_view_get_type(void)
+{
+ static GType bytes_view_type = 0;
+
+ if (!bytes_view_type) {
+ static const GTypeInfo bytes_view_info = {
+ sizeof (BytesViewClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) bytes_view_class_init,
+ NULL, /* class finalize */
+ NULL, /* class_data */
+ sizeof(BytesView),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) bytes_view_init,
+ NULL /* value_table */
+ };
+
+ bytes_view_type = g_type_register_static(GTK_TYPE_WIDGET,
+ "BytesView",
+ &bytes_view_info,
+ (GTypeFlags)0);
+ }
+ return bytes_view_type;
+}
+
+int
+bytes_view_byte_from_xy(BytesView *bv, int x, int y)
+{
+ /* hex_pos_byte array generated with hex_view_get_byte(0, 0, 0...70) */
+ static const int hex_pos_byte[70] = {
+ -1, -1,
+ 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3,
+ 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7,
+ -1,
+ 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11,
+ 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15,
+ -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ -1,
+ 8, 9, 10, 11, 12, 13, 14, 15
+ };
+
+ /* bits_pos_byte array generated with bit_view_get_byte(0, 0, 0...84) */
+ static const int bits_pos_byte[84] = {
+ -1, -1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7
+ };
+
+ int char_x, off_x = 1;
+ int char_y, off_y;
+
+ if (x < MARGIN)
+ return -1;
+
+ bytes_view_ensure_layout(bv);
+
+ char_y = bytes_view_ensure_vadj(bv)->value + (y / bv->fontsize);
+ off_y = char_y * bv->per_line;
+
+ char_x = _bytes_view_find_pos(bv, off_y, x);
+ if (/* char_x < 0 || */ char_x < bv->use_digits)
+ return -1;
+ char_x -= bv->use_digits;
+
+ switch (bv->format) {
+ case BYTES_BITS:
+ g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(bits_pos_byte), -1);
+ off_x = bits_pos_byte[char_x];
+ break;
+
+ case BYTES_HEX:
+ g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(hex_pos_byte), -1);
+ off_x = hex_pos_byte[char_x];
+ break;
+ }
+
+ if (off_x == -1)
+ return -1;
+
+ return off_y + off_x;
+}
+
+void
+bytes_view_scroll_to_byte(BytesView *bv, int byte)
+{
+ int line;
+
+ g_return_if_fail(byte >= 0 && byte < bv->len);
+
+ line = byte / bv->per_line;
+
+ bytes_view_ensure_vadj(bv);
+
+ if (line > bv->vadj->upper - bv->vadj->page_size) {
+ line = bv->vadj->upper - bv->vadj->page_size;
+
+ if (line < 0)
+ line = 0;
+ }
+
+ /* after bytes_view_scroll_to_byte() we always do bytes_view_refresh() so we can block it */
+ g_signal_handlers_block_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
+ gtk_adjustment_set_value(bv->vadj, line);
+ g_signal_handlers_unblock_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
+
+ /* XXX, scroll hadj? */
+}
+
+void
+bytes_view_set_font(BytesView *bv, PangoFontDescription *font)
+{
+ if (bv->font)
+ pango_font_description_free(bv->font);
+
+ bv->font = pango_font_description_copy(font);
+ bv->max_width = 0;
+
+ if (bv->context) {
+ g_object_unref(bv->context);
+ bv->context = NULL;
+ bytes_view_ensure_layout(bv);
+ }
+}
+
+void
+bytes_view_set_data(BytesView *bv, const guint8 *data, int len)
+{
+ g_free(bv->pd);
+ bv->pd = g_memdup(data, len);
+ bv->len = len;
+
+ /*
+ * How many of the leading digits of the offset will we supply?
+ * We always supply at least 4 digits, but if the maximum offset
+ * won't fit in 4 digits, we use as many digits as will be needed.
+ */
+ if (((len - 1) & 0xF0000000) != 0)
+ bv->use_digits = 8; /* need all 8 digits */
+ else if (((len - 1) & 0x0F000000) != 0)
+ bv->use_digits = 7; /* need 7 digits */
+ else if (((len - 1) & 0x00F00000) != 0)
+ bv->use_digits = 6; /* need 6 digits */
+ else if (((len - 1) & 0x000F0000) != 0)
+ bv->use_digits = 5; /* need 5 digits */
+ else
+ bv->use_digits = 4; /* we'll supply 4 digits */
+
+ bytes_view_ensure_vadj(bv);
+
+ bytes_view_adjustment_set(bv);
+}
+
+void
+bytes_view_set_encoding(BytesView *bv, int enc)
+{
+ g_assert(enc == PACKET_CHAR_ENC_CHAR_ASCII || enc == PACKET_CHAR_ENC_CHAR_EBCDIC);
+
+ bv->encoding = enc;
+}
+
+void
+bytes_view_set_format(BytesView *bv, int format)
+{
+ g_assert(format == BYTES_HEX || format == BYTES_BITS);
+
+ bv->format = format;
+
+ switch (format) {
+ case BYTES_BITS:
+ bv->per_line = 8;
+ break;
+
+ case BYTES_HEX:
+ bv->per_line = 16;
+ break;
+ }
+}
+
+void
+bytes_view_set_highlight_style(BytesView *bv, gboolean inverse)
+{
+ bv->bold_highlight = !inverse;
+}
+
+void
+bytes_view_set_highlight(BytesView *bv, int start, int end, guint32 mask _U_, int maskle _U_)
+{
+ bv->start[0] = start;
+ bv->end[0] = end;
+}
+
+void
+bytes_view_set_highlight_appendix(BytesView *bv, int start, int end)
+{
+ bv->start[1] = start;
+ bv->end[1] = end;
+}
+
+void
+bytes_view_refresh(BytesView *bv)
+{
+ /* bytes_view_render_full(bv); */
+ gtk_widget_queue_draw(GTK_WIDGET(bv));
+}
+
+GtkWidget *
+bytes_view_new(void)
+{
+ GtkWidget *widget;
+
+ widget = (GtkWidget *) g_object_new(BYTES_VIEW_TYPE, NULL);
+
+ g_assert(widget != NULL);
+
+ return widget;
+}
diff --git a/ui/gtk/bytes_view.h b/ui/gtk/bytes_view.h
new file mode 100644
index 0000000000..e5decee01d
--- /dev/null
+++ b/ui/gtk/bytes_view.h
@@ -0,0 +1,26 @@
+#ifndef __BYTES_VIEW_H__
+#define __BYTES_VIEW_H__
+
+#define BYTES_VIEW_TYPE (bytes_view_get_type())
+#define BYTES_VIEW(object) (G_TYPE_CHECK_INSTANCE_CAST((object), BYTES_VIEW_TYPE, BytesView))
+
+typedef struct _BytesView BytesView;
+
+GType bytes_view_get_type(void);
+
+GtkWidget *bytes_view_new(void);
+void bytes_view_set_font(BytesView *bv, PangoFontDescription *font);
+
+void bytes_view_set_data(BytesView *bv, const guint8 *data, int len);
+void bytes_view_set_encoding(BytesView *bv, int enc);
+void bytes_view_set_format(BytesView *bv, int format);
+void bytes_view_set_highlight_style(BytesView *bv, gboolean bold);
+
+void bytes_view_set_highlight(BytesView *bv, int start, int end, guint32 mask, int maskle);
+void bytes_view_set_highlight_appendix(BytesView *bv, int start, int end);
+
+void bytes_view_refresh(BytesView *bv);
+int bytes_view_byte_from_xy(BytesView *bv, int x, int y);
+void bytes_view_scroll_to_byte(BytesView *bv, int byte);
+
+#endif /* __BYTES_VIEW_H__ */
diff --git a/ui/gtk/old-gtk-compat.h b/ui/gtk/old-gtk-compat.h
index 30bdcdd4d6..50275e3069 100644
--- a/ui/gtk/old-gtk-compat.h
+++ b/ui/gtk/old-gtk-compat.h
@@ -58,6 +58,9 @@
#if !GTK_CHECK_VERSION (2, 20, 0)
# define gtk_widget_get_realized(x) GTK_WIDGET_REALIZED(x)
+# define _gtk_widget_set_realized_true(x) GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED)
+#else
+# define _gtk_widget_set_realized_true(x) gtk_widget_set_realized(x, TRUE)
#endif
#if !GTK_CHECK_VERSION (2, 22, 0)
diff --git a/ui/gtk/packet_panes.c b/ui/gtk/packet_panes.c
index d98889e4a2..c800c7bcd9 100644
--- a/ui/gtk/packet_panes.c
+++ b/ui/gtk/packet_panes.c
@@ -77,6 +77,7 @@
#include "ui/gtk/menus.h"
#include "ui/gtk/packet_panes.h"
#include "ui/gtk/proto_tree_model.h"
+#include "ui/gtk/bytes_view.h"
#ifdef _WIN32
#include <gdk/gdkwin32.h>
@@ -87,7 +88,6 @@
#define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
#define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
-#define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
#define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
#define E_BYTE_VIEW_START_KEY "byte_view_start"
#define E_BYTE_VIEW_END_KEY "byte_view_end"
@@ -243,40 +243,6 @@ collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
}
}
-#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
-#define BYTES_PER_LINE 16 /* max byte values in a line */
-#define BITS_PER_LINE 8 /* max bit values in a line */
-#define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
-#define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
-/* max number of characters hex dump takes -
- 2 digits plus trailing blank
- plus separator between first and
- second 8 digits */
-#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
-/* number of characters those bytes take;
- 3 characters per byte of hex dump,
- 2 blanks separating hex from ASCII,
- 1 character per byte of ASCII dump */
-#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
-/* number of characters per line;
- offset, 2 blanks separating offset
- from data dump, data dump */
-#define MAX_LINES 100
-#define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
-
-/* Which byte the offset is referring to. Associates
- * whitespace with the preceding digits. */
-static int
-byte_num(int offset, int start_point)
-{
- return (offset - start_point) / 3;
-}
-static int
-bit_num(int offset, int start_point)
-{
- return (offset - start_point) / 9;
-}
-
struct field_lookup_info {
field_info *fi;
GtkTreeIter iter;
@@ -312,174 +278,6 @@ GtkTreePath
return gtk_tree_model_get_path(model, &fli.iter);
}
-static int
-hex_view_get_byte(guint ndigits, int row, int column)
-{
- int byte;
- int digits_start_1;
- int digits_end_1;
- int digits_start_2;
- int digits_end_2;
- int text_start_1;
- int text_end_1;
- int text_start_2;
- int text_end_2;
-
- /*
- * The column of the first hex digit in the first half.
- * That starts after "ndigits" digits of offset and two
- * separating blanks.
- */
- digits_start_1 = ndigits + 2;
-
- /*
- * The column of the last hex digit in the first half.
- * There are BYTES_PER_LINE/2 bytes displayed in the first
- * half; there are 2 characters per byte, plus a separating
- * blank after all but the last byte's characters.
- */
- digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
- (BYTES_PER_LINE/2 - 1);
-
- /*
- * The column of the first hex digit in the second half.
- * Add 2 for the 2 separating blanks between the halves.
- */
- digits_start_2 = digits_end_1 + 2;
-
- /*
- * The column of the last hex digit in the second half.
- * Add the same value we used to get "digits_end_1" from
- * "digits_start_1".
- */
- digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
- (BYTES_PER_LINE/2 - 1);
-
- /*
- * The column of the first "text dump" character in the first half.
- * Add 3 for the 3 separating blanks between the hex and text dump.
- */
- text_start_1 = digits_end_2 + 3;
-
- /*
- * The column of the last "text dump" character in the first half.
- * There are BYTES_PER_LINE/2 bytes displayed in the first
- * half; there is 1 character per byte.
- *
- * Then subtract 1 to get the last column of the first half
- * rather than the first column after the first half.
- */
- text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
-
- /*
- * The column of the first "text dump" character in the second half.
- * Add back the 1 to get the first column after the first half,
- * and then add 1 for the separating blank between the halves.
- */
- text_start_2 = text_end_1 + 2;
-
- /*
- * The column of the last "text dump" character in second half.
- * Add the same value we used to get "text_end_1" from
- * "text_start_1".
- */
- text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
-
- /* Given the column and row, determine which byte offset
- * the user clicked on. */
- if (column >= digits_start_1 && column <= digits_end_1) {
- byte = byte_num(column, digits_start_1);
- if (byte == -1) {
- return byte;
- }
- }
- else if (column >= digits_start_2 && column <= digits_end_2) {
- byte = byte_num(column, digits_start_2);
- if (byte == -1) {
- return byte;
- }
- byte += 8;
- }
- else if (column >= text_start_1 && column <= text_end_1) {
- byte = column - text_start_1;
- }
- else if (column >= text_start_2 && column <= text_end_2) {
- byte = 8 + column - text_start_2;
- }
- else {
- /* The user didn't select a hex digit or
- * text-dump character. */
- return -1;
- }
-
- /* Add the number of bytes from the previous rows. */
- byte += row * BYTES_PER_LINE;
-
- return byte;
-}
-
-static int
-bit_view_get_byte(guint ndigits, int row, int column)
-{
- int byte;
- int digits_start;
- int digits_end;
- int text_start;
- int text_end;
-
- /*
- * The column of the first bit digit.
- * That starts after "ndigits" digits of offset and two
- * separating blanks.
- */
- digits_start = ndigits + 2;
-
- /*
- * The column of the last bit digit.
- * There are BITS_PER_LINE bytes displayed; there are
- * 8 characters per byte, plus a separating blank
- * after all but the last byte's characters.
- */
- digits_end = digits_start + (BITS_PER_LINE)*8 +
- (BITS_PER_LINE - 1);
-
- /*
- * The column of the first "text dump" character.
- * Add 3 for the 3 separating blanks between the bit and text dump.
- */
- text_start = digits_end + 3;
-
- /*
- * The column of the last "text dump" character.
- * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
- *
- * Then subtract 1 to get the last column.
- */
- text_end = text_start + BITS_PER_LINE - 1;
-
- /* Given the column and row, determine which byte offset
- * the user clicked on. */
- if (column >= digits_start && column <= digits_end) {
- byte = bit_num(column, digits_start);
- if (byte == -1) {
- return byte;
- }
- }
- else if (column >= text_start && column <= text_end) {
- byte = column - text_start;
- }
- else {
- /* The user didn't select a hex digit or
- * text-dump character. */
- return -1;
- }
-
- /* Add the number of bytes from the previous rows. */
- byte += row * BITS_PER_LINE;
-
- return byte;
-}
-
/* If the user selected a certain byte in the byte view, try to find
* the item in the GUI proto_tree that corresponds to that byte, and:
*
@@ -488,13 +286,8 @@ bit_view_get_byte(guint ndigits, int row, int column)
gboolean
byte_view_select(GtkWidget *widget, GdkEventButton *event)
{
- GtkTextView *bv = GTK_TEXT_VIEW(widget);
proto_tree *tree;
GtkTreeView *tree_view;
- GtkTextIter iter;
- int row, column;
- guint ndigits;
- gint x, y;
int byte = -1;
tvbuff_t *tvb;
@@ -508,30 +301,7 @@ byte_view_select(GtkWidget *widget, GdkEventButton *event)
tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
E_BYTE_VIEW_TREE_VIEW_PTR));
- /* get the row/column selected */
- gtk_text_view_window_to_buffer_coords(bv,
- gtk_text_view_get_window_type(bv, event->window),
- (gint) event->x, (gint) event->y, &x, &y);
- gtk_text_view_get_iter_at_location(bv, &iter, x, y);
- row = gtk_text_iter_get_line(&iter);
- column = gtk_text_iter_get_line_offset(&iter);
-
- /*
- * Get the number of digits of offset being displayed, and
- * compute the byte position in the buffer.
- */
- ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
-
- switch (recent.gui_bytes_view) {
- case BYTES_HEX:
- byte = hex_view_get_byte(ndigits, row, column);
- break;
- case BYTES_BITS:
- byte = bit_view_get_byte(ndigits, row, column);
- break;
- default:
- g_assert_not_reached();
- }
+ byte = bytes_view_byte_from_xy(BYTES_VIEW(widget), event->x, event->y);
if (byte == -1) {
return FALSE;
@@ -686,19 +456,7 @@ GtkWidget *
add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
proto_tree *tree, GtkWidget *tree_view)
{
- GtkWidget *byte_view, *byte_scrollw, *label;
- GtkTextBuffer *buf;
-#if GTK_CHECK_VERSION(3,0,0)
- GtkStyleContext *context;
- GdkRGBA *rgba_bg_color;
- GdkRGBA *rgba_fg_color;
-#if !GTK_CHECK_VERSION(3,2,0)
- GdkColor bg_color;
- GdkColor fg_color;
-#endif /* GTK_CHECK_VERSION(3,2,0) */
-#else
- GtkStyle *style;
-#endif
+ GtkWidget *byte_view, *byte_scrollw, *label;
/* Byte view. Create a scrolled window for the text. */
byte_scrollw = scrolled_window_new(NULL, NULL);
@@ -710,49 +468,9 @@ add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
gtk_widget_show(byte_scrollw);
- byte_view = gtk_text_view_new();
- gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
- gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
- buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
-#if GTK_CHECK_VERSION(3,0,0)
- context = gtk_widget_get_style_context (GTK_WIDGET(byte_view));
- gtk_style_context_get (context, GTK_STATE_FLAG_SELECTED,
- GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &rgba_bg_color,
- GTK_STYLE_PROPERTY_COLOR, &rgba_fg_color,
- NULL);
-#if GTK_CHECK_VERSION(3,2,0)
- gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
- gtk_text_buffer_create_tag(buf, "reverse",
- "font-desc", user_font_get_regular(),
- "foreground-gdk", rgba_fg_color,
- "background-gdk", rgba_bg_color,
- NULL);
-#else
- /* Hack */
- bg_color.red = rgba_bg_color->red * 65535;
- bg_color.green = rgba_bg_color->green * 65535;
- bg_color.blue = rgba_bg_color->blue * 65535;
- fg_color.red = rgba_fg_color->red * 65535;
- fg_color.green = rgba_fg_color->green * 65535;
- fg_color.blue = rgba_fg_color->blue * 65535;
-
- gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
- gtk_text_buffer_create_tag(buf, "reverse",
- "font-desc", user_font_get_regular(),
- "foreground-gdk", &fg_color,
- "background-gdk", &bg_color,
- NULL);
-#endif /* GTK_CHECK_VERSION(3,2,0) */
-#else
- style = gtk_widget_get_style(GTK_WIDGET(top_level));
- gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
- gtk_text_buffer_create_tag(buf, "reverse",
- "font-desc", user_font_get_regular(),
- "foreground-gdk", &style->text[GTK_STATE_SELECTED],
- "background-gdk", &style->base[GTK_STATE_SELECTED],
- NULL);
-#endif
- gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
+ byte_view = bytes_view_new();
+ bytes_view_set_font(BYTES_VIEW(byte_view), user_font_get_regular());
+
g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
@@ -1153,459 +871,23 @@ savehex_cb(GtkWidget * w _U_, gpointer data _U_)
}
#endif
-static GtkTextMark *
-packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int bstart, int bend, guint32 mask, int mask_le, int use_digits, int create_mark)
-{
- GtkTextIter i_start, i_stop, iter;
-
- GtkTextTag *revstyle_tag;
- const char *revstyle;
-
- int per_line = 0;
- int per_one = 0;
- int bits_per_one = 0;
- int hex_offset, ascii_offset;
-
- int start_line, start_line_pos;
- int stop_line, stop_line_pos;
-
- if (bstart == -1 || bend == -1)
- return NULL;
-
- /* Display with inverse video ? */
- if (prefs.gui_hex_dump_highlight_style)
- revstyle = "reverse";
- else
- revstyle = "bold";
-
- revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle);
-
- switch (recent.gui_bytes_view) {
- case BYTES_HEX:
- per_line = BYTES_PER_LINE;
- per_one = 2+1; /* "ff " */
- bits_per_one = 4;
- break;
- case BYTES_BITS:
- per_line = BITS_PER_LINE;
- per_one = 8+1; /* "10101010 " */
- bits_per_one = 1;
- break;
- default:
- g_assert_not_reached();
- }
-
- start_line = bstart / per_line;
- start_line_pos = bstart % per_line;
-
- stop_line = bend / per_line;
- stop_line_pos = bend % per_line;
-
-#define hex_fix(pos) hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line)
-#define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line)
-
- hex_offset = use_digits + 2;
- ascii_offset = hex_fix(per_line) + 2;
-
- gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos));
-
- if (mask == 0x00) {
- while (start_line <= stop_line) {
- int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
- int first_block_adjust = (recent.gui_bytes_view == BYTES_HEX) ? (line_pos_end == per_line/2) : 0;
-
- if (start_line_pos == line_pos_end) break;
-
- /* bits/hex */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1-first_block_adjust);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
-
- /* ascii */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end)-first_block_adjust);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
-
- start_line_pos = 0;
- start_line++;
- }
-
- } else if (mask_le) { /* LSB of mask first (little-endian) */
- while (start_line <= stop_line) {
- int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
- int line_pos = start_line_pos;
-
- while (line_pos < line_pos_end) {
- int lop = 8 / bits_per_one;
- int mask_per_one = (1 << bits_per_one) - 1;
- int ascii_on = 0;
-
- while (lop--) {
- if ((mask & mask_per_one)) {
- /* bits/hex */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
-
- ascii_on = 1;
- }
- mask >>= bits_per_one;
- }
-
- /* at least one bit of ascii was one -> turn ascii on */
- if (ascii_on) {
- /* ascii */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
- }
-
- if (!mask)
- goto xend;
-
- line_pos++;
- }
-
- start_line_pos = 0;
- start_line++;
- }
- } else { /* mask starting from end (big-endian) */
- while (start_line <= stop_line) {
- int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
- int line_pos = stop_line_pos-1;
-
- while (line_pos >= line_pos_start) {
- int lop = 8 / bits_per_one;
- int mask_per_one = (1 << bits_per_one) - 1;
- int ascii_on = 0;
-
- while (lop--) {
- if ((mask & mask_per_one)) {
- /* bits/hex */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
-
- ascii_on = 1;
- }
- mask >>= bits_per_one;
- }
-
- /* at least one bit of ascii was one -> turn ascii on */
- if (ascii_on) {
- /* ascii */
- gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
- gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
- gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
- }
-
- if (!mask)
- goto xend;
-
- line_pos--;
- }
-
- stop_line_pos = per_line;
- stop_line--;
- }
- }
-xend:
- return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
-#undef hex_fix
-#undef ascii_fix
-}
-
-/* Update the progress bar this many times when reading a file. */
-#define N_PROGBAR_UPDATES 100
-/* The minimum packet length required to check if a progress bar is needed or not */
-#define MIN_PACKET_LENGTH 1024
-
-/*
- * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
- * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
- * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
- * presumably why there's a progress bar for it.)
- *
- * Perhaps what's needed is a custom widget (either one that lets you stuff
- * text into it more quickly, or one that's a "virtual" widget so that the
- * text for a row is constructed, via a callback, when the row is to be
- * displayed). A custom widget might also let us treat the offset, hex
- * data, and ASCII data as three columns, so you can select purely in
- * the hex dump column.
- */
-static void
-packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
-{
- int i = 0, j, k = 0, b, cur;
- guchar line[MAX_LINES_LEN + 1];
- static guchar hexchars[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
- static const guint8 bitmask[8] = {
- 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
- guchar c = '\0';
- unsigned int use_digits;
- GtkTextIter iter;
-
- progdlg_t *progbar = NULL;
- float progbar_val;
- gboolean progbar_stop_flag;
- GTimeVal progbar_start_time;
- gchar progbar_status_str[100];
- int progbar_nextstep;
- int progbar_quantum;
-
- gtk_text_buffer_set_text(buf, "", 0);
- gtk_text_buffer_get_start_iter(buf, &iter);
-
- /*
- * How many of the leading digits of the offset will we supply?
- * We always supply at least 4 digits, but if the maximum offset
- * won't fit in 4 digits, we use as many digits as will be needed.
- */
- if (((len - 1) & 0xF0000000) != 0)
- use_digits = 8; /* need all 8 digits */
- else if (((len - 1) & 0x0F000000) != 0)
- use_digits = 7; /* need 7 digits */
- else if (((len - 1) & 0x00F00000) != 0)
- use_digits = 6; /* need 6 digits */
- else if (((len - 1) & 0x000F0000) != 0)
- use_digits = 5; /* need 5 digits */
- else
- use_digits = 4; /* we'll supply 4 digits */
-
- /* Record the number of digits in this text view. */
- g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
-
- /* Update the progress bar when it gets to this value. */
- if (len > MIN_PACKET_LENGTH){
- progbar_nextstep = 0;
- }else{
- /* If length =< MIN_PACKET_LENGTH
- * there is no need to calculate the progress
- */
- progbar_nextstep = len+1;
- }
-
- /* When we reach the value that triggers a progress bar update,
- bump that value by this amount. */
- progbar_quantum = len/N_PROGBAR_UPDATES;
- /* Progress so far. */
- progbar_val = 0.0f;
-
- progbar_stop_flag = FALSE;
- g_get_current_time(&progbar_start_time);
-
- cur = 0;
- while (i < len) {
- /* Create the progress bar if necessary.
- We check on every iteration of the loop, so that it takes no
- longer than the standard time to create it (otherwise, for a
- large packet, we might take considerably longer than that standard
- time in order to get to the next progress bar step). */
- if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
- progbar = delayed_create_progress_dlg("Processing", "Packet Details",
- TRUE,
- &progbar_stop_flag,
- &progbar_start_time,
- progbar_val);
-
- /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
- when we update it, we have to run the GTK+ main loop to get it
- to repaint what's pending, and doing so may involve an "ioctl()"
- to see if there's any pending input from an X server, and doing
- that for every packet can be costly, especially on a big file. */
- if (i >= progbar_nextstep) {
-
- if (progbar != NULL) {
- /* let's not divide by zero. I should never be started
- * with count == 0, so let's assert that
- */
- g_assert(len > 0);
- progbar_val = (gfloat) i / len;
- g_snprintf(progbar_status_str, sizeof(progbar_status_str),
- "%4u of %u bytes", i, len);
- update_progress_dlg(progbar, progbar_val, progbar_status_str);
- }
-
- progbar_nextstep += progbar_quantum;
- }
-
- if (progbar_stop_flag) {
- /* Well, the user decided to abort the operation. Just stop,
- and arrange to return TRUE to our caller, so they know it
- was stopped explicitly. */
- break;
- }
-
- /* Print the line number */
- j = use_digits;
- do {
- j--;
- c = (i >> (j*4)) & 0xF;
- line[cur++] = hexchars[c];
- } while (j != 0);
- line[cur++] = ' ';
- line[cur++] = ' ';
-
- j = i;
- switch (recent.gui_bytes_view) {
- case BYTES_HEX:
- k = i + BYTES_PER_LINE;
- break;
- case BYTES_BITS:
- k = i + BITS_PER_LINE;
- break;
- default:
- g_assert_not_reached();
- }
- /* Print the hex bit */
- while (i < k) {
- if (i < len) {
- switch (recent.gui_bytes_view) {
- case BYTES_HEX:
- line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
- line[cur++] = hexchars[pd[i] & 0x0f];
- break;
- case BYTES_BITS:
- for (b = 0; b < 8; b++) {
- line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
- }
- break;
- default:
- g_assert_not_reached();
- }
- } else {
- switch (recent.gui_bytes_view) {
- case BYTES_HEX:
- line[cur++] = ' '; line[cur++] = ' ';
- break;
- case BYTES_BITS:
- for (b = 0; b < 8; b++) {
- line[cur++] = ' ';
- }
- break;
- default:
- g_assert_not_reached();
- }
- }
- i++;
- /* Inter byte space if not at end of line */
- if (i < k) {
- line[cur++] = ' ';
- /* insert a space every BYTE_VIEW_SEP bytes */
- if( ( i % BYTE_VIEW_SEP ) == 0 ) {
- line[cur++] = ' ';
- }
- }
- }
-
- /* Print some space at the end of the line */
- line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
-
- /* Print the ASCII bit */
- i = j;
-
- while (i < k) {
- if (i < len) {
- if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
- c = pd[i];
- }
- else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
- c = EBCDIC_to_ASCII1(pd[i]);
- }
- else {
- g_assert_not_reached();
- }
- line[cur++] = isprint(c) ? c : '.';
- } else {
- line[cur++] = ' ';
- }
- i++;
- if (i < k) {
- /* insert a space every BYTE_VIEW_SEP bytes */
- if( ( i % BYTE_VIEW_SEP ) == 0 ) {
- line[cur++] = ' ';
- }
- }
- }
- line[cur++] = '\n';
- if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
- gtk_text_buffer_insert(buf, &iter, line, cur);
- cur = 0;
- }
- }
-
- /* We're done printing the packets; destroy the progress bar if
- it was created. */
- if (progbar != NULL)
- destroy_progress_dlg(progbar);
-
- if (cur) {
- gtk_text_buffer_insert(buf, &iter, line, cur);
- }
-}
-
static void
packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
int bend, guint32 bmask, int bmask_le,
int astart, int aend, int encoding)
{
- GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
- GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
- GtkTextBuffer *buf_tmp;
- GtkTextMark *mark;
- int ndigits;
-
- GtkTextIter start, end;
-
- g_object_ref(buf);
-
-#if 0
- /* XXX: Setting the text_view buffer to NULL is apparently
- * not a good idea; If a progress_bar is displayed below
- * in delayed_create_progress_dlg() there will then be
- * a crash internally in the gtk library.
- * (It appears that gtk_text_view_set_buffer
- * queues a callback to be run when this
- * thread is next idle. Unfortunately the call to
- * gtk_main_iteration() in delayed_create_progress_dlg()
- * causes the internal callback to be run which then
- * crashes (because the textview has no buffer ?))
- */
- gtk_text_view_set_buffer(bv_text_view, NULL);
-#endif
-
- /* attach a dummy buffer in place of the real buffer.
- * (XXX: Presumably this is done so there's no attempt
- * to display the real buffer until it has been
- * completely generated).
- */
- buf_tmp = gtk_text_buffer_new(NULL);
- gtk_text_view_set_buffer(bv_text_view, buf_tmp);
- g_object_unref(buf_tmp);
-
- packet_hex_print_common(buf, bv, pd, len, encoding);
-
- /* mark everything with "plain" tag */
- gtk_text_buffer_get_start_iter(buf, &start);
- gtk_text_buffer_get_end_iter(buf, &end);
- gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
-
- ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
-
- /* mark reverse tags */
- mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
- packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
-
- gtk_text_view_set_buffer(bv_text_view, buf);
-
- /* scroll text into position */
- if (mark) {
- gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
- gtk_text_buffer_delete_mark(buf, mark);
- }
- g_object_unref(buf);
+ bytes_view_set_encoding(BYTES_VIEW(bv), encoding);
+ bytes_view_set_format(BYTES_VIEW(bv), recent.gui_bytes_view);
+ bytes_view_set_data(BYTES_VIEW(bv), pd, len);
+
+ bytes_view_set_highlight_style(BYTES_VIEW(bv), prefs.gui_hex_dump_highlight_style);
+
+ bytes_view_set_highlight(BYTES_VIEW(bv), bstart, bend, bmask, bmask_le);
+ bytes_view_set_highlight_appendix(BYTES_VIEW(bv), astart, aend);
+
+ if (bstart != -1 && bend != -1)
+ bytes_view_scroll_to_byte(BYTES_VIEW(bv), bstart);
+ bytes_view_refresh(BYTES_VIEW(bv));
}
void