Don't call gtk_widget_unregister_window() in bytes_view_unrealize() after GTK+ 3.8.0
[metze/wireshark/wip.git] / ui / gtk / bytes_view.c
1 /* bytes_view.c
2  *
3  * $Id$
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
22  * USA.
23  */
24
25 /* Code based on:
26  *   xtext, the text widget used by X-Chat. Peter Zelezny <zed@xchat.org>.
27  *   GtkTextView. Copyright (C) 2000 Red Hat, Inc.
28  *   GtkHex. Jaka Mocnik <jaka@gnu.org>.
29  *   pango-layout.c: High-level layout driver. Copyright (C) 2000, 2001, 2006 Red Hat Software
30  */
31
32 #include "config.h"
33
34 #include <gtk/gtk.h>
35 #include "ui/gtk/old-gtk-compat.h"
36
37 #include <string.h>
38
39 #include "../isprint.h"
40
41 #include <epan/charsets.h>
42 #include <epan/packet.h>
43 #include <epan/prefs.h>
44
45 #include "packet_panes.h"
46
47 #define MARGIN 2
48 #define REFRESH_TIMEOUT 10
49
50 static GtkWidgetClass *parent_class = NULL;
51
52 struct _BytesView
53 {
54         GtkWidget widget;
55
56         PangoContext *context;
57
58         PangoFontDescription *font;
59         int font_ascent;
60         int font_descent;
61         int fontsize;
62
63         GtkAdjustment *vadj;
64         GtkAdjustment *hadj;
65 #if GTK_CHECK_VERSION(3, 0, 0)
66         guint hscroll_policy : 1;
67         guint vscroll_policy : 1;
68 #endif
69         gint adj_tag;
70         int max_width;
71
72         gboolean bold_highlight;
73         int state;
74
75 /* data */
76         packet_char_enc encoding;       /* ASCII or EBCDIC */
77         bytes_view_type format;         /* bytes in hex or bytes as bits */
78         guint8 *pd;                     /* packet data */
79         int len;                        /* length of packet data in bytes */
80 /* data-highlight */
81         int start[2];
82         int end[2];
83
84         int per_line;                   /* number of bytes shown per line */
85         int use_digits;                 /* number of hex digits of byte offset */
86 };
87
88 #include "bytes_view.h"
89
90 typedef struct _BytesViewClass
91 {
92         GtkWidgetClass parent_class;
93
94         void (*set_scroll_adjustments)(BytesView *, GtkAdjustment *, GtkAdjustment *);
95
96 } BytesViewClass;
97
98 static void bytes_view_set_scroll_adjustments(BytesView *, GtkAdjustment *, GtkAdjustment *);
99 static void bytes_view_adjustment_set(BytesView *);
100
101 static void
102 bytes_view_init(BytesView *bv)
103 {
104         bv->context = NULL;
105
106         bv->encoding = PACKET_CHAR_ENC_CHAR_ASCII;
107         bv->format = BYTES_HEX;
108
109         bv->per_line = 16;
110         bv->use_digits = 4;
111
112         bv->max_width = 0;
113 }
114
115 static void
116 bytes_view_destroy(BytesView *bv)
117 {
118         if (bv->pd) {
119                 g_free(bv->pd);
120                 bv->pd = NULL;
121         }
122         if (bv->adj_tag) {
123                 g_source_remove(bv->adj_tag);
124                 bv->adj_tag = 0;
125         }
126         if (bv->vadj) {
127                 g_object_unref(G_OBJECT(bv->vadj));
128                 bv->vadj = NULL;
129         }
130         if (bv->hadj) {
131                 g_object_unref(G_OBJECT(bv->hadj));
132                 bv->hadj = NULL;
133         }
134         if (bv->font) {
135                 pango_font_description_free(bv->font);
136                 bv->font = NULL;
137         }
138         if (bv->context) {
139                 g_object_unref(bv->context);
140                 bv->context = NULL;
141         }
142 }
143
144 #if GTK_CHECK_VERSION(3, 0, 0)
145 static void
146 bytes_view_destroy_widget(GtkWidget *widget)
147 {
148         bytes_view_destroy(BYTES_VIEW(widget));
149
150         GTK_WIDGET_CLASS(parent_class)->destroy(widget);
151 }
152
153 #else
154
155 static void
156 bytes_view_destroy_object(GtkObject *object)
157 {
158         bytes_view_destroy(BYTES_VIEW(object));
159
160         if (GTK_OBJECT_CLASS(parent_class)->destroy)
161                 (*GTK_OBJECT_CLASS(parent_class)->destroy)(object);
162 }
163
164 #endif
165
166 static void
167 bytes_view_ensure_layout(BytesView *bv)
168 {
169         if (bv->context == NULL) {
170                 bv->context = gtk_widget_get_pango_context(GTK_WIDGET(bv));
171                 g_object_ref(bv->context);
172
173                 {
174                 PangoLanguage *lang;
175                 PangoFontMetrics *metrics;
176
177                         /* vte and xchat does it this way */
178                         lang = pango_context_get_language(bv->context);
179                         metrics = pango_context_get_metrics(bv->context, bv->font, lang);
180                         bv->font_ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
181                         bv->font_descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
182                         pango_font_metrics_unref(metrics);
183
184                         bv->fontsize = bv->font_ascent + bv->font_descent;
185                 }
186                 g_assert(bv->context);
187                 bytes_view_adjustment_set(bv);
188         }
189 }
190
191 static void
192 bytes_view_realize(GtkWidget *widget)
193 {
194         BytesView *bv;
195         GdkWindowAttr attributes;
196         GtkAllocation allocation;
197         GdkWindow *win;
198
199 #if GTK_CHECK_VERSION(3, 0, 0)
200         GtkStyleContext *context;
201 #endif
202
203         _gtk_widget_set_realized_true(widget);
204         bv = BYTES_VIEW(widget);
205
206         gtk_widget_get_allocation(widget, &allocation);
207
208         attributes.window_type = GDK_WINDOW_CHILD;
209         attributes.x = allocation.x;
210         attributes.y = allocation.y;
211         attributes.width = allocation.width;
212         attributes.height = allocation.height;
213         attributes.wclass = GDK_INPUT_OUTPUT;
214         attributes.visual = gtk_widget_get_visual(widget);
215         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
216
217 #if !GTK_CHECK_VERSION(3, 0, 0)
218         attributes.colormap = gtk_widget_get_colormap(widget);
219
220         win = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP);
221 #else
222         win = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL);
223 #endif
224
225         gtk_widget_set_window(widget, win);
226
227 #if GTK_CHECK_VERSION(3, 8, 0)
228         gtk_widget_register_window(widget, win);
229 #else
230         gdk_window_set_user_data(win, widget);
231 #endif
232
233 #if !GTK_CHECK_VERSION(3, 0, 0) /* XXX, check */
234         gdk_window_set_back_pixmap(win, NULL, FALSE);
235 #endif
236
237 #if GTK_CHECK_VERSION(3, 0, 0)
238         context = gtk_widget_get_style_context(widget);
239         gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
240         /* gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY); */
241
242 #elif GTK_CHECK_VERSION(2, 20, 0)
243         gtk_widget_style_attach(widget);
244 #else
245         widget->style = gtk_style_attach(widget->style, win);
246 #endif
247         bytes_view_ensure_layout(bv);
248 }
249
250 static void
251 bytes_view_unrealize(GtkWidget *widget)
252 {
253         BytesView *bv = BYTES_VIEW(widget);
254
255         if (bv->context) {
256                 g_object_unref(bv->context);
257                 bv->context = NULL;
258         }
259         /* if there are still events in the queue, this'll avoid segfault */
260 #if !GTK_CHECK_VERSION(3, 8, 0)
261         gdk_window_set_user_data(gtk_widget_get_window(widget), NULL);
262 #endif
263
264         if (parent_class->unrealize)
265                 (*GTK_WIDGET_CLASS(parent_class)->unrealize)(widget);
266 }
267
268 static GtkAdjustment *
269 bytes_view_ensure_vadj(BytesView *bv)
270 {
271         if (bv->vadj == NULL) {
272                 bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj);
273                 g_assert(bv->vadj != NULL);
274         }
275         return bv->vadj;
276 }
277
278 static GtkAdjustment *
279 bytes_view_ensure_hadj(BytesView *bv)
280 {
281         if (bv->hadj == NULL) {
282                 bytes_view_set_scroll_adjustments(bv, bv->hadj, bv->vadj);
283                 g_assert(bv->hadj != NULL);
284         }
285         return bv->hadj;
286 }
287
288 static gboolean
289 bytes_view_scroll(GtkWidget *widget, GdkEventScroll *event)
290 {
291         BytesView *bv = BYTES_VIEW(widget);
292
293         gdouble new_value;
294
295         if (event->direction == GDK_SCROLL_UP) {        /* mouse wheel pageUp */
296                 bytes_view_ensure_vadj(bv);
297
298                 new_value = gtk_adjustment_get_value(bv->vadj) - (gtk_adjustment_get_page_increment(bv->vadj) / 10);
299                 if (new_value < gtk_adjustment_get_lower(bv->vadj))
300                         new_value = gtk_adjustment_get_lower(bv->vadj);
301                 gtk_adjustment_set_value(bv->vadj, new_value);
302
303         } else if (event->direction == GDK_SCROLL_DOWN) {       /* mouse wheel pageDn */
304                 bytes_view_ensure_vadj(bv);
305
306                 new_value = gtk_adjustment_get_value(bv->vadj) + (gtk_adjustment_get_page_increment(bv->vadj) / 10);
307                 if (new_value > (gtk_adjustment_get_upper(bv->vadj) - gtk_adjustment_get_page_size(bv->vadj)))
308                         new_value = gtk_adjustment_get_upper(bv->vadj) - gtk_adjustment_get_page_size(bv->vadj);
309                 gtk_adjustment_set_value(bv->vadj, new_value);
310         }
311         return FALSE;
312 }
313
314 static void
315 bytes_view_allocate(GtkWidget *widget, GtkAllocation *allocation)
316 {
317         gtk_widget_set_allocation(widget, allocation);
318
319         if (gtk_widget_get_realized(widget)) {
320                 BytesView *bv = BYTES_VIEW(widget);
321
322                 gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
323                 bytes_view_adjustment_set(bv);
324         }
325 }
326
327 #if GTK_CHECK_VERSION(3, 0, 0)
328 static void
329 bytes_view_get_preferred_width(GtkWidget *widget _U_, gint *minimum, gint *natural)
330 {
331         *minimum = *natural = 200;
332 }
333
334 static void
335 bytes_view_get_preferred_height(GtkWidget *widget _U_, gint *minimum, gint *natural)
336 {
337         *minimum = *natural = 90;
338 }
339
340 #else
341
342 static void
343 bytes_view_size_request(GtkWidget *widget _U_, GtkRequisition *requisition)
344 {
345         requisition->width = 200;
346         requisition->height = 90;
347 }
348 #endif
349
350 static GSList *
351 _pango_runs_build(BytesView *bv, const char *str, int len)
352 {
353         GSList *runs = NULL;
354
355         PangoAttrList *attrs;
356         PangoAttrIterator *iter;
357
358         GList *run_list;
359         GList *tmp_list;
360
361         attrs = pango_attr_list_new();
362         pango_attr_list_insert_before(attrs, pango_attr_font_desc_new(bv->font));
363
364         iter = pango_attr_list_get_iterator(attrs);
365
366         run_list = pango_itemize(bv->context, str, 0, len, attrs, iter);
367
368         for (tmp_list = run_list; tmp_list; tmp_list = tmp_list->next) {
369                 PangoLayoutRun *run = g_slice_new(PangoLayoutRun);
370                 PangoItem *run_item = (PangoItem *)tmp_list->data;
371
372                 run->item = run_item;
373
374                 /* XXX pango_layout_get_item_properties(run_item, &state->properties); */
375
376                 run->glyphs = pango_glyph_string_new();
377                 pango_shape(str + run_item->offset, run_item->length, &run_item->analysis, run->glyphs);
378
379                 runs = g_slist_prepend(runs, run);
380         }
381
382         g_list_free(run_list);
383
384         pango_attr_iterator_destroy(iter);
385         pango_attr_list_unref(attrs);
386
387         return g_slist_reverse(runs);
388 }
389
390 static int
391 _pango_glyph_string_to_pixels(PangoGlyphString *glyphs, PangoFont *font _U_)
392 {
393 #if PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 14
394         return pango_glyph_string_get_width(glyphs) / PANGO_SCALE;
395 #else
396         PangoRectangle logical_rect;
397
398         pango_glyph_string_extents(glyphs, font, NULL, &logical_rect);
399         /*  pango_extents_to_pixels(&logical_rect, NULL); */
400
401         return (logical_rect.width / PANGO_SCALE);
402 #endif
403 }
404
405 static int
406 xtext_draw_layout_line(cairo_t *cr, gint x, gint y, GSList *runs)
407 {
408         while (runs) {
409                 PangoLayoutRun *run = (PangoLayoutRun *)runs->data;
410
411                 cairo_move_to(cr, x, y);
412                 pango_cairo_show_glyph_string(cr, run->item->analysis.font, run->glyphs);
413
414                 x += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
415                 runs = runs->next;
416         }
417         return x;
418 }
419
420 static int
421 _pango_runs_width(GSList *runs)
422 {
423         int width = 0;
424
425         while (runs) {
426                 PangoLayoutRun *run = (PangoLayoutRun *)runs->data;
427
428                 width += _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
429                 runs = runs->next;
430         }
431         return width;
432 }
433
434 static void
435 _pango_runs_free(GSList *runs)
436 {
437         GSList *list = runs;
438
439         while (list) {
440                 PangoLayoutRun *run = (PangoLayoutRun *)list->data;
441
442                 pango_item_free(run->item);
443                 pango_glyph_string_free(run->glyphs);
444                 g_slice_free(PangoLayoutRun, run);
445
446                 list = list->next;
447         }
448         g_slist_free(runs);
449 }
450
451 typedef int bytes_view_line_cb(BytesView *, void *data, int x, int arg1, const char *str, int len);
452
453 static int
454 bytes_view_flush_render(BytesView *bv, void *data, int x, int y, const char *str, int len)
455 {
456         cairo_t *cr = (cairo_t *)data;
457         GSList *line_runs;
458         int str_width;
459
460 #if GTK_CHECK_VERSION(3, 0, 0)
461         GtkStyleContext *context;
462         GdkRGBA bg_color, fg_color;
463 #endif
464
465         if (len < 1)
466                 return 0;
467
468         line_runs = _pango_runs_build(bv, str, len);
469
470         /* XXX, cliping */
471
472 #if GTK_CHECK_VERSION(3, 0, 0)
473         context = gtk_widget_get_style_context(GTK_WIDGET(bv));
474 #endif
475
476         if (bv->state == GTK_STATE_SELECTED) {
477                 str_width = _pango_runs_width(line_runs);
478
479                 /* background */
480 #if GTK_CHECK_VERSION(3, 0, 0)
481                 gtk_style_context_get_background_color(context, (GtkStateFlags)(GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_SELECTED), &bg_color);
482                 gdk_cairo_set_source_rgba(cr, &bg_color);
483 #else
484                 gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->base[bv->state]);
485 #endif
486                 cairo_rectangle(cr, x, y - bv->font_ascent, str_width, bv->fontsize);
487                 cairo_fill(cr);
488         }
489
490         /* text */
491 #if GTK_CHECK_VERSION(3, 0, 0)
492         gtk_style_context_get_color(context, (GtkStateFlags)(GTK_STATE_FLAG_FOCUSED | (bv->state == GTK_STATE_SELECTED ? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_NORMAL)), &fg_color);
493         gdk_cairo_set_source_rgba(cr, &fg_color);
494 #else
495         gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->text[bv->state]);
496 #endif
497         str_width = xtext_draw_layout_line(cr, x, y, line_runs)-x;
498
499         _pango_runs_free(line_runs);
500
501         return str_width;
502 }
503
504 static int
505 _pango_runs_find_index(GSList *runs, int x_pos, const char *str)
506 {
507         int start_pos = 0;
508
509         while (runs) {
510                 PangoLayoutRun *run = (PangoLayoutRun *)runs->data;
511                 int width;
512
513                 width = _pango_glyph_string_to_pixels(run->glyphs, run->item->analysis.font);
514
515                 if (x_pos >= start_pos && x_pos < start_pos + width) {
516                         gboolean char_trailing;
517                         int pos;
518
519                         pango_glyph_string_x_to_index(run->glyphs,
520                                         (char *) str + run->item->offset, run->item->length,
521                                         &run->item->analysis,
522                                         (x_pos - start_pos) * PANGO_SCALE,
523                                         &pos, &char_trailing);
524
525                         return run->item->offset + pos;
526                 }
527
528                 start_pos += width;
529                 runs = runs->next;
530         }
531         return -1;
532 }
533
534 static int
535 bytes_view_flush_pos(BytesView *bv, void *data, int x, int search_x, const char *str, int len)
536 {
537         int *pos_x = (int *)data;
538         GSList *line_runs;
539         int line_width;
540
541         if (len < 1)
542                 return 0;
543
544         line_runs = _pango_runs_build(bv, str, len);
545
546         line_width = _pango_runs_width(line_runs);
547
548         if (x <= search_x && x + line_width > search_x) {
549                 int off_x = search_x - x;
550                 int pos_run;
551
552                 if ((pos_run = _pango_runs_find_index(line_runs, off_x, str)) != -1)
553                         *pos_x = (-*pos_x) + pos_run;
554
555                 return -1;      /* terminate */
556         } else
557                 *pos_x -= len;
558
559         _pango_runs_free(line_runs);
560
561         return line_width;
562 }
563
564 static void
565 bytes_view_render_state(BytesView *bv, int state)
566 {
567         g_assert(state == GTK_STATE_NORMAL || state == GTK_STATE_SELECTED);
568
569         if (bv->bold_highlight) {
570                 pango_font_description_set_weight(bv->font,
571                                 (state == GTK_STATE_SELECTED) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
572                 bv->state = GTK_STATE_NORMAL;
573         } else
574                 bv->state = state;
575 }
576
577 #define BYTE_VIEW_SEP    8      /* insert a space every BYTE_VIEW_SEP bytes */
578
579 static void
580 _bytes_view_line_common(BytesView *bv, void *data, const int org_off, int xx, int arg1, bytes_view_line_cb flush)
581 {
582         static const guchar hexchars[16] = {
583                 '0', '1', '2', '3', '4', '5', '6', '7',
584                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
585
586         const guint8 *pd = bv->pd;
587         const int len = bv->len;
588
589         int state;
590
591         char str[128];
592         int cur = 0;
593
594         int off;
595         guchar c;
596         int byten;
597         int j;
598
599         int scroll_x;
600         int dx;
601
602         g_assert(org_off >= 0);
603
604         scroll_x = (int) gtk_adjustment_get_value(bytes_view_ensure_hadj(bv));
605
606         state = GTK_STATE_NORMAL;
607         bytes_view_render_state(bv, GTK_STATE_NORMAL);
608
609         /* Print the line number */
610         j = bv->use_digits;
611         do {
612                 j--;
613                 c = (org_off >> (j*4)) & 0xF;
614                 str[cur++] = hexchars[c];
615         } while (j != 0);
616         str[cur++] = ' ';
617         str[cur++] = ' ';
618
619         /* Print the hex bit */
620         for (byten = 0, off = org_off; byten < bv->per_line; byten++) {
621                 gboolean byte_highlighted =
622                         (off >= bv->start[0] && off < bv->end[0]) ||
623                         (off >= bv->start[1] && off < bv->end[1]);
624                 int state_cur = (off < len && byte_highlighted) ?
625                                 GTK_STATE_SELECTED : GTK_STATE_NORMAL;
626
627                 if (state_cur != state) {
628                         if (state == GTK_STATE_NORMAL && byten) {
629                                 str[cur++] = ' ';
630                                 /* insert a space every BYTE_VIEW_SEP bytes */
631                                 if ((off % BYTE_VIEW_SEP) == 0)
632                                         str[cur++] = ' ';
633                         }
634
635                         if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
636                                 return;
637                         xx += dx;
638                         cur = 0;
639                         bytes_view_render_state(bv, state_cur);
640                         state = state_cur;
641
642                         if (state == GTK_STATE_NORMAL && byten) {
643                                 str[cur++] = ' ';
644                                 /* insert a space every BYTE_VIEW_SEP bytes */
645                                 if ((off % BYTE_VIEW_SEP) == 0)
646                                         str[cur++] = ' ';
647                         }
648
649                 } else if (byten) {
650                         str[cur++] = ' ';
651                         /* insert a space every BYTE_VIEW_SEP bytes */
652                         if ((off % BYTE_VIEW_SEP) == 0)
653                                 str[cur++] = ' ';
654                 }
655
656                 if (off < len) {
657                         switch (bv->format) {
658                                 case BYTES_HEX:
659                                         str[cur++] = hexchars[(pd[off] & 0xf0) >> 4];
660                                         str[cur++] = hexchars[pd[off] & 0x0f];
661                                         break;
662                                 case BYTES_BITS:
663                                         /* XXX, bitmask */
664                                         for (j = 7; j >= 0; j--)
665                                                 str[cur++] = (pd[off] & (1 << j)) ? '1' : '0';
666                                         break;
667                         }
668                 } else {
669                         switch (bv->format) {
670                                 case BYTES_HEX:
671                                         str[cur++] = ' ';
672                                         str[cur++] = ' ';
673                                         break;
674                                 case BYTES_BITS:
675                                         for (j = 7; j >= 0; j--)
676                                                 str[cur++] = ' ';
677                                         break;
678                         }
679                 }
680                 off++;
681         }
682
683         if (state != GTK_STATE_NORMAL) {
684                 if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
685                         return;
686                 xx += dx;
687                 cur = 0;
688                 bytes_view_render_state(bv, GTK_STATE_NORMAL);
689                 state = GTK_STATE_NORMAL;
690         }
691
692         /* Print some space at the end of the line */
693         str[cur++] = ' '; str[cur++] = ' '; str[cur++] = ' ';
694
695         /* Print the ASCII bit */
696         for (byten = 0, off = org_off; byten < bv->per_line; byten++) {
697                 gboolean byte_highlighted =
698                         (off >= bv->start[0] && off < bv->end[0]) ||
699                         (off >= bv->start[1] && off < bv->end[1]);
700                 int state_cur = (off < len && byte_highlighted) ?
701                                 GTK_STATE_SELECTED : GTK_STATE_NORMAL;
702
703                 if (state_cur != state) {
704                         if (state == GTK_STATE_NORMAL && byten) {
705                                 /* insert a space every BYTE_VIEW_SEP bytes */
706                                 if ((off % BYTE_VIEW_SEP) == 0)
707                                         str[cur++] = ' ';
708                         }
709
710                         if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
711                                 return;
712                         xx += dx;
713                         cur = 0;
714                         bytes_view_render_state(bv, state_cur);
715                         state = state_cur;
716
717                         if (state == GTK_STATE_NORMAL && byten) {
718                                 /* insert a space every BYTE_VIEW_SEP bytes */
719                                 if ((off % BYTE_VIEW_SEP) == 0)
720                                         str[cur++] = ' ';
721                         }
722
723                 } else if (byten) {
724                         /* insert a space every BYTE_VIEW_SEP bytes */
725                         if ((off % BYTE_VIEW_SEP) == 0)
726                                 str[cur++] = ' ';
727                 }
728
729                 if (off < len) {
730                         c = (bv->encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) ?
731                                 EBCDIC_to_ASCII1(pd[off]) :
732                                 pd[off];
733
734                         str[cur++] = isprint(c) ? c : '.';
735                 } else
736                         str[cur++] = ' ';
737
738                 off++;
739         }
740
741         if (cur) {
742                 if ((dx = flush(bv, data, xx - scroll_x, arg1, str, cur)) < 0)
743                         return;
744                 xx += dx;
745                 /* cur = 0; */
746         }
747
748         if (state != GTK_STATE_NORMAL) {
749                 bytes_view_render_state(bv, GTK_STATE_NORMAL);
750                 /* state = GTK_STATE_NORMAL; */
751         }
752
753         if (bv->max_width < xx)
754                 bv->max_width = xx;
755 }
756
757 static void
758 _bytes_view_render_line(BytesView *bv, cairo_t *cr, const int org_off, int yy)
759 {
760         _bytes_view_line_common(bv, cr, org_off, MARGIN, yy, bytes_view_flush_render);
761 }
762
763 static int
764 _bytes_view_find_pos(BytesView *bv, const int org_off, int search_x)
765 {
766         int pos_x = 0;
767
768         _bytes_view_line_common(bv, &pos_x, org_off, MARGIN, search_x, bytes_view_flush_pos);
769
770         return pos_x;
771 }
772
773 static void
774 bytes_view_render(BytesView *bv, cairo_t *cr, GdkRectangle *area)
775 {
776         const int old_max_width = bv->max_width;
777
778         int width, height;
779         int y;
780         int off;
781
782         guint line, lines_max;
783         guint lines_max_full;
784
785 #if GTK_CHECK_VERSION(3, 0, 0)
786         GtkStyleContext *context;
787         GdkRGBA bg_color;
788 #endif
789
790         if (!gtk_widget_get_realized(GTK_WIDGET(bv)))
791                 return;
792
793         bytes_view_ensure_layout(bv);
794
795 #if GTK_CHECK_VERSION(3, 0, 0)
796         width = gtk_widget_get_allocated_width(GTK_WIDGET(bv));
797         height = gtk_widget_get_allocated_height(GTK_WIDGET(bv));
798 #elif GTK_CHECK_VERSION(2,24,0)
799         width = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(bv)));
800         height = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(bv)));
801 #else
802         gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(bv)), &width, &height);
803 #endif
804
805         if (width < 32 + MARGIN || height < bv->fontsize)
806                 return;
807
808         if (area) {
809                 line = area->y / bv->fontsize;
810                 lines_max = 1 + (area->y + area->height) / bv->fontsize;
811         } else {
812                 line = 0;
813                 lines_max = (guint) -1;
814         }
815         /* g_print("from %d to %d\n", line, lines_max); */
816
817         y = (bv->fontsize * line);
818
819         /* clear */
820 #if GTK_CHECK_VERSION(3, 0, 0)
821         context = gtk_widget_get_style_context(GTK_WIDGET(bv));
822         gtk_style_context_get_background_color(context, (GtkStateFlags)(GTK_STATE_FLAG_FOCUSED | GTK_STATE_FLAG_NORMAL), &bg_color);
823         gdk_cairo_set_source_rgba(cr, &bg_color);
824 #else
825         gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(bv))->base[GTK_STATE_NORMAL]);
826 #endif
827         if (area)
828                 cairo_rectangle(cr, area->x, area->y, area->x + area->width, area->y + area->height);
829         else
830                 cairo_rectangle(cr, 0, 0, width, height);
831         cairo_fill(cr);
832
833         if (bv->pd) {
834                 guint real_line = line + (guint) gtk_adjustment_get_value(bytes_view_ensure_vadj(bv));
835
836                 lines_max_full = (height / bv->fontsize) + 1;
837                 if (lines_max_full < lines_max)
838                         lines_max = lines_max_full;
839
840                 off = real_line * bv->per_line;
841
842                 while (off < bv->len) {
843                         _bytes_view_render_line(bv, cr, off, y + bv->font_ascent);
844                         line++;
845                         if (line >= lines_max)
846                                 break;
847
848                         off += bv->per_line;
849                         y += bv->fontsize;
850                 }
851         }
852
853         if (old_max_width != bv->max_width)
854                 bytes_view_adjustment_set(bv);
855 }
856
857 static void
858 bytes_view_render_full(BytesView *bv)
859 {
860         cairo_t *cr;
861
862         if (bv->adj_tag) {
863                 g_source_remove(bv->adj_tag);
864                 bv->adj_tag = 0;
865         }
866
867         cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(bv)));
868         bytes_view_render(bv, cr, NULL);
869         cairo_destroy(cr);
870 }
871
872 #if GTK_CHECK_VERSION(3, 0, 0)
873 static gboolean
874 bytes_view_draw(GtkWidget *widget, cairo_t *cr)
875 {
876         GdkRectangle area;
877
878         gdk_cairo_get_clip_rectangle(cr, &area);
879
880         bytes_view_render(BYTES_VIEW(widget), cr, &area);
881         return FALSE;
882 }
883
884 #else
885
886 static gboolean
887 bytes_view_expose(GtkWidget *widget, GdkEventExpose *event)
888 {
889         BytesView *bv = BYTES_VIEW(widget);
890         cairo_t *cr;
891
892         cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(bv)));
893
894         gdk_cairo_region(cr, event->region);
895         cairo_clip(cr);
896
897         bytes_view_render(bv, cr, &event->area);
898
899         cairo_destroy(cr);
900         return FALSE;
901 }
902
903 #endif
904
905 #if !GTK_CHECK_VERSION(2, 14, 0)
906 static void
907 _gtk_adjustment_configure(GtkAdjustment *adj,
908                           gdouble        value,
909                           gdouble        lower,
910                           gdouble        upper,
911                           gdouble        step_increment,
912                           gdouble        page_increment,
913                           gdouble        page_size)
914 {
915         adj->value = value;
916         adj->lower = lower;
917         adj->upper = upper;
918         adj->step_increment = step_increment;
919         adj->page_increment = page_increment;
920         adj->page_size = page_size;
921
922         gtk_adjustment_changed(adj);
923 }
924
925 #elif GTK_CHECK_VERSION(3, 0, 0)
926
927 #define _gtk_adjustment_configure(adj, val, low, up, step, page, size) \
928         gtk_adjustment_configure(adj, val, low, MAX((up), (size)), step, page, size)
929
930 #else
931
932 #define _gtk_adjustment_configure(adj, val, low, up, step, page, size) \
933         gtk_adjustment_configure(adj, val, low, up, step, page, size)
934
935 #endif
936
937
938 static void
939 bytes_view_adjustment_set(BytesView *bv)
940 {
941         GtkAllocation allocation;
942         double lower, upper, page_size, step_increment, page_increment, value;
943
944         if (bv->vadj == NULL || bv->hadj == NULL)
945                 return;
946
947         if (bv->context == NULL) {
948                 bytes_view_ensure_layout(bv);
949                 /* bytes_view_ensure_layout will call bytes_view_adjustment_set() again */
950                 return;
951         }
952
953         gtk_widget_get_allocation(GTK_WIDGET(bv), &allocation);
954
955         if (bv->vadj) {
956                 lower = 0;
957                 upper = (int) (bv->len / bv->per_line);
958                 if ((bv->len % bv->per_line))
959                         upper++;
960
961                 page_size = (allocation.height - bv->font_descent) / bv->fontsize;
962                 page_increment = page_size;
963                 step_increment = 1;
964
965                 value = gtk_adjustment_get_value(bv->vadj);
966
967                 if (value > upper - page_size)
968                         value = upper - page_size;
969
970                 if (value < 0)
971                         value = 0;
972
973                 _gtk_adjustment_configure(bv->vadj, value, lower, upper, step_increment, page_increment, page_size);
974         }
975
976         if (bv->hadj) {
977                 lower = 0;
978                 upper = bv->max_width;
979
980                 page_size = allocation.width;
981                 page_increment = page_size;
982                 step_increment = page_size / 10.0;
983
984                 value = gtk_adjustment_get_value(bv->hadj);
985
986                 if (value > upper - page_size)
987                         value = upper - page_size;
988
989                 if (value < 0)
990                         value = 0;
991
992                 _gtk_adjustment_configure(bv->hadj, value, lower, upper, step_increment, page_increment, page_size);
993         }
994 }
995
996 static gint
997 bytes_view_adjustment_timeout(BytesView *bv)
998 {
999         bv->adj_tag = 0;
1000         bytes_view_render_full(bv);
1001         return 0;
1002 }
1003
1004 static void
1005 bytes_view_adjustment_changed(GtkAdjustment *adj, BytesView *bv)
1006 {
1007         /*  delay rendering when scrolling (10ms) */
1008         if (adj && ((adj == bv->vadj) || (adj == bv->hadj))) {
1009                 if (!bv->adj_tag)
1010                         bv->adj_tag = g_timeout_add(REFRESH_TIMEOUT, (GSourceFunc) bytes_view_adjustment_timeout, bv);
1011         }
1012 }
1013
1014 static void
1015 bytes_view_set_scroll_adjustments(BytesView *bv, GtkAdjustment *hadj, GtkAdjustment *vadj)
1016 {
1017         gboolean need_adjust = FALSE;
1018
1019         g_return_if_fail(!hadj || GTK_IS_ADJUSTMENT(hadj));
1020         g_return_if_fail(!vadj || GTK_IS_ADJUSTMENT(vadj));
1021
1022         if (!vadj)
1023                 vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1024
1025         if (!hadj)
1026                 hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
1027
1028         if (bv->vadj && (bv->vadj != vadj)) {
1029                 g_signal_handlers_disconnect_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
1030                 g_object_unref(bv->vadj);
1031         }
1032         if (bv->vadj != vadj) {
1033                 bv->vadj = vadj;
1034                 g_object_ref_sink(bv->vadj);
1035
1036                 g_signal_connect(bv->vadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv);
1037                 need_adjust = TRUE;
1038 #if GTK_CHECK_VERSION(3, 0, 0)
1039                 g_object_notify(G_OBJECT(bv), "vadjustment");
1040 #endif
1041         }
1042
1043         if (bv->hadj && (bv->hadj != hadj)) {
1044                 g_signal_handlers_disconnect_by_func(bv->hadj, bytes_view_adjustment_changed, bv);
1045                 g_object_unref(bv->hadj);
1046         }
1047         if (bv->hadj != hadj) {
1048                 bv->hadj = hadj;
1049                 g_object_ref_sink(bv->hadj);
1050
1051                 g_signal_connect(bv->hadj, "value-changed", G_CALLBACK(bytes_view_adjustment_changed), bv);
1052                 need_adjust = TRUE;
1053 #if GTK_CHECK_VERSION(3, 0, 0)
1054                 g_object_notify(G_OBJECT(bv), "hadjustment");
1055 #endif
1056         }
1057
1058         if (need_adjust)
1059                 bytes_view_adjustment_set(bv);
1060 }
1061
1062 #if GTK_CHECK_VERSION(3, 0, 0)
1063 enum {
1064         PROP_0,
1065         PROP_HADJUSTMENT,
1066         PROP_VADJUSTMENT,
1067         PROP_HSCROLL_POLICY,
1068         PROP_VSCROLL_POLICY
1069 };
1070
1071 static void
1072 bytes_view_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
1073 {
1074         BytesView *bv = BYTES_VIEW(object);
1075
1076         switch (prop_id) {
1077                 case PROP_HADJUSTMENT:
1078                         bytes_view_set_scroll_adjustments(bv, (GtkAdjustment *)g_value_get_object(value), bv->vadj);
1079                         break;
1080
1081                 case PROP_VADJUSTMENT:
1082                         bytes_view_set_scroll_adjustments(bv, bv->hadj, (GtkAdjustment *)g_value_get_object(value));
1083                         break;
1084
1085                 case PROP_HSCROLL_POLICY:
1086                         bv->hscroll_policy = g_value_get_enum(value);
1087                         gtk_widget_queue_resize(GTK_WIDGET(bv));
1088                         break;
1089
1090                 case PROP_VSCROLL_POLICY:
1091                         bv->vscroll_policy = g_value_get_enum(value);
1092                         gtk_widget_queue_resize(GTK_WIDGET(bv));
1093                         break;
1094
1095                 default:
1096                         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1097                         break;
1098         }
1099 }
1100
1101 static void
1102 bytes_view_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1103 {
1104         BytesView *bv = BYTES_VIEW(object);
1105
1106         switch (prop_id) {
1107                 case PROP_HADJUSTMENT:
1108                         g_value_set_object(value, bv->hadj);
1109                         break;
1110
1111                 case PROP_VADJUSTMENT:
1112                         g_value_set_object(value, bv->vadj);
1113                         break;
1114
1115                 case PROP_HSCROLL_POLICY:
1116                         g_value_set_enum(value, bv->hscroll_policy);
1117                         break;
1118
1119                 case PROP_VSCROLL_POLICY:
1120                         g_value_set_enum(value, bv->vscroll_policy);
1121                         break;
1122
1123                 default:
1124                         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1125                         break;
1126         }
1127 }
1128
1129 #else
1130
1131 /* bv_VOID__OBJECT_OBJECT() generated by:
1132  *   $ echo 'VOID:OBJECT,OBJECT' | glib-genmarshal --prefix=bv --body
1133  * (glib-genmarshal version 2.32.4)
1134  * I *really hope* it's portable over platforms and can be put generated.
1135  */
1136
1137 static void
1138 bv_VOID__OBJECT_OBJECT(GClosure *closure, GValue *return_value _U_, guint n_params, const GValue *param_values, gpointer hint _U_, gpointer marshal_data)
1139 {
1140         typedef void (*GMarshalFunc_VOID__OBJECT_OBJECT)(gpointer, gpointer, gpointer, gpointer);
1141
1142         register GMarshalFunc_VOID__OBJECT_OBJECT callback;
1143         register GCClosure *cc = (GCClosure*) closure;
1144         register gpointer data1, data2;
1145
1146         g_return_if_fail(n_params == 3);
1147
1148         if (G_CCLOSURE_SWAP_DATA(closure)) {
1149                 data1 = closure->data;
1150                 data2 = g_value_peek_pointer(param_values + 0);
1151         } else {
1152                 data1 = g_value_peek_pointer(param_values + 0);
1153                 data2 = closure->data;
1154         }
1155         callback = (GMarshalFunc_VOID__OBJECT_OBJECT) (marshal_data ? marshal_data : cc->callback);
1156
1157         callback(data1, g_value_get_object(param_values + 1), g_value_get_object(param_values + 2), data2);
1158 }
1159
1160 #endif
1161
1162 static void
1163 bytes_view_class_init(BytesViewClass *klass)
1164 {
1165 #if !GTK_CHECK_VERSION(3, 0, 0)
1166         GtkObjectClass *object_class;
1167 #endif
1168         GtkWidgetClass *widget_class;
1169
1170         parent_class = (GtkWidgetClass *) g_type_class_peek_parent(klass);
1171
1172 #if !GTK_CHECK_VERSION(3, 0, 0)
1173         object_class = (GtkObjectClass *) klass;
1174 #endif
1175         widget_class = (GtkWidgetClass *) klass;
1176
1177 #if GTK_CHECK_VERSION(3, 0, 0)
1178         widget_class->destroy = bytes_view_destroy_widget;
1179 #else
1180         object_class->destroy = bytes_view_destroy_object;
1181 #endif
1182         widget_class->realize = bytes_view_realize;
1183         widget_class->unrealize = bytes_view_unrealize;
1184 #if GTK_CHECK_VERSION(3, 0, 0)
1185         widget_class->get_preferred_width = bytes_view_get_preferred_width;
1186         widget_class->get_preferred_height = bytes_view_get_preferred_height;
1187 #else
1188         widget_class->size_request = bytes_view_size_request;
1189 #endif
1190         widget_class->size_allocate = bytes_view_allocate;
1191
1192 #if GTK_CHECK_VERSION(3, 0, 0)
1193         widget_class->draw = bytes_view_draw;
1194 #else
1195         widget_class->expose_event = bytes_view_expose;
1196 #endif
1197
1198         widget_class->scroll_event = bytes_view_scroll;
1199
1200 #if GTK_CHECK_VERSION(3, 0, 0)
1201         {
1202                 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1203
1204                 gobject_class->set_property = bytes_view_set_property;
1205                 gobject_class->get_property = bytes_view_get_property;
1206
1207                 /* XXX, move some code from widget->destroy to gobject->finalize? */
1208                 /* gobject_class->finalize = bytes_view_finalize; */
1209
1210                 g_object_class_override_property(gobject_class, PROP_HADJUSTMENT,    "hadjustment");
1211                 g_object_class_override_property(gobject_class, PROP_VADJUSTMENT,    "vadjustment");
1212                 g_object_class_override_property(gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
1213                 g_object_class_override_property(gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
1214         }
1215 #else
1216         klass->set_scroll_adjustments = bytes_view_set_scroll_adjustments;
1217
1218         widget_class->set_scroll_adjustments_signal =
1219                 g_signal_new(g_intern_static_string("set-scroll-adjustments"),
1220                         G_OBJECT_CLASS_TYPE(object_class),
1221                         (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
1222                         G_STRUCT_OFFSET(BytesViewClass, set_scroll_adjustments),
1223                         NULL, NULL,
1224                         bv_VOID__OBJECT_OBJECT,
1225                         G_TYPE_NONE, 2,
1226                         GTK_TYPE_ADJUSTMENT,
1227                         GTK_TYPE_ADJUSTMENT);
1228 #endif
1229 }
1230
1231 GType
1232 bytes_view_get_type(void)
1233 {
1234         static GType bytes_view_gtype = 0;
1235
1236         if (!bytes_view_gtype) {
1237                 static const GTypeInfo bytes_view_info = {
1238                         sizeof (BytesViewClass),
1239                         NULL, /* base_init */
1240                         NULL, /* base_finalize */
1241                         (GClassInitFunc) bytes_view_class_init,
1242                         NULL, /* class finalize */
1243                         NULL, /* class_data */
1244                         sizeof(BytesView),
1245                         0, /* n_preallocs */
1246                         (GInstanceInitFunc) bytes_view_init,
1247                         NULL /* value_table */
1248                 };
1249
1250 #if GTK_CHECK_VERSION(3, 0, 0)
1251                 static const GInterfaceInfo scrollable_info = {
1252                         NULL,
1253                         NULL,
1254                         NULL
1255                 };
1256 #endif
1257
1258                 bytes_view_gtype = g_type_register_static(GTK_TYPE_WIDGET,
1259                                                           "BytesView",
1260                                                           &bytes_view_info,
1261                                                           (GTypeFlags)0);
1262
1263 #if GTK_CHECK_VERSION(3, 0, 0)
1264                 g_type_add_interface_static(bytes_view_gtype,
1265                                             GTK_TYPE_SCROLLABLE,
1266                                             &scrollable_info);
1267 #endif
1268         }
1269         return bytes_view_gtype;
1270 }
1271
1272 int
1273 bytes_view_byte_from_xy(BytesView *bv, int x, int y)
1274 {
1275         /* hex_pos_byte array generated with hex_view_get_byte(0, 0, 0...70) */
1276         static const int hex_pos_byte[70] = {
1277                 -1, -1,
1278                 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3,
1279                 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7,
1280                 -1,
1281                 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11,
1282                 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15,
1283                 -1, -1,
1284                 0, 1, 2, 3, 4, 5, 6, 7,
1285                 -1,
1286                 8, 9, 10, 11, 12, 13, 14, 15
1287         };
1288
1289         /* bits_pos_byte array generated with bit_view_get_byte(0, 0, 0...84) */
1290         static const int bits_pos_byte[84] = {
1291                 -1, -1,
1292                 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1293                 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1294                 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
1295                 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1296                 -1, -1,
1297                 0, 1, 2, 3, 4, 5, 6, 7
1298         };
1299
1300         int char_x, off_x = 1;
1301         int char_y, off_y;
1302
1303         if (x < MARGIN)
1304                 return -1;
1305
1306         bytes_view_ensure_layout(bv);
1307
1308         char_y = (int) gtk_adjustment_get_value(bytes_view_ensure_vadj(bv)) + (y / bv->fontsize);
1309         off_y = char_y * bv->per_line;
1310
1311         char_x = _bytes_view_find_pos(bv, off_y, x);
1312         if (/* char_x < 0 || */ char_x < bv->use_digits)
1313                 return -1;
1314         char_x -= bv->use_digits;
1315
1316         switch (bv->format) {
1317                 case BYTES_BITS:
1318                         g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(bits_pos_byte), -1);
1319                         off_x = bits_pos_byte[char_x];
1320                         break;
1321
1322                 case BYTES_HEX:
1323                         g_return_val_if_fail(char_x >= 0 && char_x < (int) G_N_ELEMENTS(hex_pos_byte), -1);
1324                         off_x = hex_pos_byte[char_x];
1325                         break;
1326         }
1327
1328         if (off_x == -1)
1329                 return -1;
1330
1331         return off_y + off_x;
1332 }
1333
1334 void
1335 bytes_view_scroll_to_byte(BytesView *bv, int byte)
1336 {
1337         int line;
1338
1339         g_return_if_fail(byte >= 0 && byte < bv->len);
1340
1341         line = byte / bv->per_line;
1342
1343         bytes_view_ensure_vadj(bv);
1344
1345         if (line > gtk_adjustment_get_upper(bv->vadj) - gtk_adjustment_get_page_size(bv->vadj)) {
1346                 line = (int)(gtk_adjustment_get_upper(bv->vadj) - gtk_adjustment_get_page_size(bv->vadj));
1347
1348                 if (line < 0)
1349                         line = 0;
1350         }
1351
1352         /* after bytes_view_scroll_to_byte() we always do bytes_view_refresh() so we can block it */
1353         g_signal_handlers_block_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
1354         gtk_adjustment_set_value(bv->vadj, line);
1355         g_signal_handlers_unblock_by_func(bv->vadj, bytes_view_adjustment_changed, bv);
1356
1357         /* XXX, scroll hadj? */
1358 }
1359
1360 void
1361 bytes_view_set_font(BytesView *bv, PangoFontDescription *font)
1362 {
1363         if (bv->font)
1364                 pango_font_description_free(bv->font);
1365
1366         bv->font = pango_font_description_copy(font);
1367         bv->max_width = 0;
1368
1369         if (bv->context) {
1370                 g_object_unref(bv->context);
1371                 bv->context = NULL;
1372                 bytes_view_ensure_layout(bv);
1373         }
1374 }
1375
1376 void
1377 bytes_view_set_data(BytesView *bv, const guint8 *data, int len)
1378 {
1379         g_free(bv->pd);
1380         bv->pd = (guint8 *)g_memdup(data, len);
1381         bv->len = len;
1382
1383         /*
1384          * How many of the leading digits of the offset will we supply?
1385          * We always supply at least 4 digits, but if the maximum offset
1386          * won't fit in 4 digits, we use as many digits as will be needed.
1387          */
1388         if (((len - 1) & 0xF0000000) != 0)
1389                 bv->use_digits = 8; /* need all 8 digits */
1390         else if (((len - 1) & 0x0F000000) != 0)
1391                 bv->use_digits = 7; /* need 7 digits */
1392         else if (((len - 1) & 0x00F00000) != 0)
1393                 bv->use_digits = 6; /* need 6 digits */
1394         else if (((len - 1) & 0x000F0000) != 0)
1395                 bv->use_digits = 5; /* need 5 digits */
1396         else
1397                 bv->use_digits = 4; /* we'll supply 4 digits */
1398
1399         bytes_view_ensure_vadj(bv);
1400
1401         bytes_view_adjustment_set(bv);
1402 }
1403
1404 void
1405 bytes_view_set_encoding(BytesView *bv, int enc)
1406 {
1407         g_assert(enc == PACKET_CHAR_ENC_CHAR_ASCII || enc == PACKET_CHAR_ENC_CHAR_EBCDIC);
1408
1409         bv->encoding = (packet_char_enc)enc;
1410 }
1411
1412 void
1413 bytes_view_set_format(BytesView *bv, int format)
1414 {
1415         g_assert(format == BYTES_HEX || format == BYTES_BITS);
1416
1417         bv->format = (bytes_view_type)format;
1418
1419         switch (format) {
1420                 case BYTES_BITS:
1421                         bv->per_line = 8;
1422                         break;
1423
1424                 case BYTES_HEX:
1425                         bv->per_line = 16;
1426                         break;
1427         }
1428 }
1429
1430 void
1431 bytes_view_set_highlight_style(BytesView *bv, gboolean inverse)
1432 {
1433         bv->bold_highlight = !inverse;
1434 }
1435
1436 void
1437 bytes_view_set_highlight(BytesView *bv, int start, int end, guint32 mask _U_, int maskle _U_)
1438 {
1439         bv->start[0] = start;
1440         bv->end[0] = end;
1441 }
1442
1443 void
1444 bytes_view_set_highlight_appendix(BytesView *bv, int start, int end)
1445 {
1446         bv->start[1] = start;
1447         bv->end[1] = end;
1448 }
1449
1450 void
1451 bytes_view_refresh(BytesView *bv)
1452 {
1453         /* bytes_view_render_full(bv); */
1454         gtk_widget_queue_draw(GTK_WIDGET(bv));
1455 }
1456
1457 GtkWidget *
1458 bytes_view_new(void)
1459 {
1460         GtkWidget *widget;
1461
1462         widget = (GtkWidget *) g_object_new(BYTES_VIEW_TYPE, NULL);
1463
1464         g_assert(widget != NULL);
1465
1466         return widget;
1467 }