Removed trailing whitespaces from .h and .c files using the
[obnox/wireshark/wip.git] / gtk / proto_draw.c
1 /* proto_draw.c
2  * Routines for GTK+ packet display
3  *
4  * $Id: proto_draw.c,v 1.57 2002/08/28 21:03:49 jmayer Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * Jeff Foster,    2001/03/12,  added support for displaying named
11  *                              data sources as tabbed hex windows
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <ctype.h>
33 #include <stdarg.h>
34
35 #include <gtk/gtk.h>
36 #include <gdk/gdkkeysyms.h>
37
38 #include <stdio.h>
39 #include <string.h>
40
41 #include <epan/epan_dissect.h>
42
43 #include "main.h"
44 #include <epan/packet.h>
45 #include "util.h"
46 #include "menu.h"
47 #include "keys.h"
48
49 #include "colors.h"
50 #include "prefs.h"
51 #include "proto_draw.h"
52 #include "packet_win.h"
53 #include "ui_util.h"
54 #include "gtkglobals.h"
55
56 #define BYTE_VIEW_WIDTH    16
57 #define BYTE_VIEW_SEP      8
58
59 #define E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
60 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
61 #define E_BYTE_VIEW_NDIGITS_KEY   "byte_view_ndigits"
62 #define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
63 #define E_BYTE_VIEW_START_KEY     "byte_view_start"
64 #define E_BYTE_VIEW_END_KEY       "byte_view_end"
65 #define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
66
67 static GtkWidget *
68 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
69     proto_tree *tree, GtkWidget *tree_view);
70
71 static void
72 proto_tree_draw_node(GNode *node, gpointer data);
73
74 /* Get the current text window for the notebook. */
75 GtkWidget *
76 get_notebook_bv_ptr(GtkWidget *nb_ptr)
77 {
78   int num;
79   GtkWidget *bv_page;
80
81   num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
82   bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
83   return GTK_BIN(bv_page)->child;
84 }
85
86 /*
87  * Get the data and length for a byte view, given the byte view page.
88  * Return the pointer, or NULL on error, and set "*data_len" to the length.
89  */
90 const guint8 *
91 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
92 {
93   tvbuff_t *byte_view_tvb;
94   const guint8 *data_ptr;
95
96   byte_view_tvb = gtk_object_get_data(GTK_OBJECT(byte_view),
97                                       E_BYTE_VIEW_TVBUFF_KEY);
98   if (byte_view_tvb == NULL)
99     return NULL;
100
101   data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
102   *data_len = tvb_length(byte_view_tvb);
103   return data_ptr;
104 }
105
106 /*
107  * Set the current text window for the notebook to the window that
108  * refers to a particular tvbuff.
109  */
110 void
111 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
112 {
113   int num;
114   GtkWidget *bv_page, *bv;
115   tvbuff_t *bv_tvb;
116
117   for (num = 0;
118        (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
119        num++) {
120     bv = GTK_BIN(bv_page)->child;
121     bv_tvb = gtk_object_get_data(GTK_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
122     if (bv_tvb == tvb) {
123       /* Found it. */
124       gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
125       break;
126     }
127   }
128 }
129
130 /* Redraw a given byte view window. */
131 void
132 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
133 {
134   GtkWidget *bv;
135   const guint8 *data;
136   guint len;
137
138   bv = get_notebook_bv_ptr(nb);
139   if (bv != NULL) {
140     data = get_byte_view_data_and_length(bv, &len);
141     if (data != NULL)
142       packet_hex_print(GTK_TEXT(bv), data, fd, finfo, len);
143   }
144 }
145
146 /* Redraw all byte view windows. */
147 void
148 redraw_hex_dump_all(void)
149 {
150   if (cfile.current_frame != NULL)
151     redraw_hex_dump( byte_nb_ptr, cfile.current_frame, finfo_selected);
152
153   redraw_hex_dump_packet_wins();
154 }
155
156 static void
157 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
158 {
159         field_info      *finfo;
160
161         finfo = gtk_ctree_node_get_row_data( ctree, node);
162         g_assert(finfo);
163
164         /*
165          * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
166          * are thus presumably leaf nodes and cannot be expanded.
167          */
168         if (finfo->tree_type != -1) {
169                 g_assert(finfo->tree_type >= 0 &&
170                     finfo->tree_type < num_tree_types);
171                 tree_is_expanded[finfo->tree_type] = TRUE;
172         }
173 }
174
175 static void
176 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
177 {
178         field_info      *finfo;
179
180         finfo = gtk_ctree_node_get_row_data( ctree, node);
181         g_assert(finfo);
182
183         /*
184          * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
185          * are thus presumably leaf nodes and cannot be collapsed.
186          */
187         if (finfo->tree_type != -1) {
188                 g_assert(finfo->tree_type >= 0 &&
189                     finfo->tree_type < num_tree_types);
190                 tree_is_expanded[finfo->tree_type] = FALSE;
191         }
192 }
193
194 static void
195 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
196 {
197         if (event->keyval != GDK_Return)
198                 return;
199         gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
200 }
201
202 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
203 #define BYTES_PER_LINE  16      /* max byte values in a line */
204 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3 + 1)
205                                 /* max number of characters hex dump takes -
206                                    2 digits plus trailing blank
207                                    plus separator between first and
208                                    second 8 digits */
209 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
210                                 /* number of characters those bytes take;
211                                    3 characters per byte of hex dump,
212                                    2 blanks separating hex from ASCII,
213                                    1 character per byte of ASCII dump */
214 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
215                                 /* number of characters per line;
216                                    offset, 2 blanks separating offset
217                                    from data dump, data dump */
218
219 /* Which byte the offset is referring to. Associates
220  * whitespace with the preceding digits. */
221 static int
222 byte_num(int offset, int start_point)
223 {
224         return (offset - start_point) / 3;
225 }
226
227 /* If the user selected a certain byte in the byte view, try to find
228  * the item in the GUI proto_tree that corresponds to that byte, and
229  * select it. */
230 static gint
231 byte_view_select(GtkWidget *widget, GdkEventButton *event)
232 {
233         proto_tree      *tree;
234         GtkCTree        *ctree;
235         GtkCTreeNode    *node, *parent;
236         field_info      *finfo;
237         GtkText         *bv = GTK_TEXT(widget);
238         int             row, column;
239         int             byte;
240         tvbuff_t        *tvb;
241         guint           ndigits;
242         int             digits_start_1;
243         int             digits_end_1;
244         int             digits_start_2;
245         int             digits_end_2;
246         int             text_start_1;
247         int             text_end_1;
248         int             text_start_2;
249         int             text_end_2;
250
251         /*
252          * Get the number of digits of offset being displayed, and
253          * compute the columns of various parts of the display.
254          */
255         ndigits = GPOINTER_TO_UINT(gtk_object_get_data(GTK_OBJECT(bv),
256                           E_BYTE_VIEW_NDIGITS_KEY));
257
258         /*
259          * The column of the first hex digit in the first half.
260          * That starts after "ndigits" digits of offset and two
261          * separating blanks.
262          */
263         digits_start_1 = ndigits + 2;
264
265         /*
266          * The column of the last hex digit in the first half.
267          * There are BYTES_PER_LINE/2 bytes displayed in the first
268          * half; there are 2 characters per byte, plus a separating
269          * blank after all but the last byte's characters.
270          *
271          * Then subtract 1 to get the last column of the first half
272          * rather than the first column after the first half.
273          */
274         digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
275             (BYTES_PER_LINE/2 - 1) - 1;
276
277         /*
278          * The column of the first hex digit in the second half.
279          * Add back the 1 to get the first column after the first
280          * half, and then add 2 for the 2 separating blanks between
281          * the halves.
282          */
283         digits_start_2 = digits_end_1 + 3;
284
285         /*
286          * The column of the last hex digit in the second half.
287          * Add the same value we used to get "digits_end_1" from
288          * "digits_start_1".
289          */
290         digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
291             (BYTES_PER_LINE/2 - 1) - 1;
292
293         /*
294          * The column of the first "text dump" character in the first half.
295          * Add back the 1 to get the first column after the second
296          * half's hex dump, and then add 3 for the 3 separating blanks
297          * between the hex and text dummp.
298          */
299         text_start_1 = digits_end_2 + 4;
300
301         /*
302          * The column of the last "text dump" character in the first half.
303          * There are BYTES_PER_LINE/2 bytes displayed in the first
304          * half; there is 1 character per byte.
305          *
306          * Then subtract 1 to get the last column of the first half
307          * rather than the first column after the first half.
308          */
309         text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
310
311         /*
312          * The column of the first "text dump" character in the second half.
313          * Add back the 1 to get the first column after the first half,
314          * and then add 1 for the separating blank between the halves.
315          */
316         text_start_2 = text_end_1 + 2;
317
318         /*
319          * The column of the last "text dump" character in second half.
320          * Add the same value we used to get "text_end_1" from
321          * "text_start_1".
322          */
323         text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
324
325         tree = gtk_object_get_data(GTK_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
326         if (tree == NULL) {
327                 /*
328                  * Somebody clicked on the dummy byte view; do nothing.
329                  */
330                 return FALSE;
331         }
332         ctree = GTK_CTREE(gtk_object_get_data(GTK_OBJECT(widget),
333                                               E_BYTE_VIEW_TREE_VIEW_PTR));
334
335         /* Given the mouse (x,y) and the current GtkText (h,v)
336          * adjustments, and the size of the font, figure out
337          * which text column/row the user selected. This could be off
338          * if the bold version of the font is bigger than the
339          * regular version of the font. */
340         column = (bv->hadj->value + event->x) / m_font_width;
341         row = (bv->vadj->value + event->y) / m_font_height;
342
343         /* Given the column and row, determine which byte offset
344          * the user clicked on. */
345         if (column >= digits_start_1 && column <= digits_end_1) {
346                 byte = byte_num(column, digits_start_1);
347                 if (byte == -1) {
348                         return FALSE;
349                 }
350         }
351         else if (column >= digits_start_2 && column <= digits_end_2) {
352                 byte = byte_num(column, digits_start_2);
353                 if (byte == -1) {
354                         return FALSE;
355                 }
356                 byte += 8;
357         }
358         else if (column >= text_start_1 && column <= text_end_1) {
359                 byte = column - text_start_1;
360         }
361         else if (column >= text_start_2 && column <= text_end_2) {
362                 byte = 8 + column - text_start_2;
363         }
364         else {
365                 /* The user didn't select a hex digit or
366                  * text-dump character. */
367                 return FALSE;
368         }
369
370         /* Add the number of bytes from the previous rows. */
371         byte += row * 16;
372
373         /* Get the data source tvbuff */
374         tvb = gtk_object_get_data(GTK_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
375
376         /* Find the finfo that corresponds to our byte. */
377         finfo = proto_find_field_from_offset(tree, byte, tvb);
378
379         if (!finfo) {
380                 return FALSE;
381         }
382
383         node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
384         g_assert(node);
385
386         /* Expand and select our field's row */
387         gtk_ctree_expand(ctree, node);
388         gtk_ctree_select(ctree, node);
389         expand_tree(ctree, node, NULL);
390
391         /* ... and its parents */
392         parent = GTK_CTREE_ROW(node)->parent;
393         while (parent) {
394                 gtk_ctree_expand(ctree, parent);
395                 expand_tree(ctree, parent, NULL);
396                 parent = GTK_CTREE_ROW(parent)->parent;
397         }
398
399         /* And position the window so the selection is visible.
400          * Position the selection in the middle of the viewable
401          * pane. */
402         gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
403
404         return FALSE;
405 }
406
407 /* Calls functions for different mouse-button presses. */
408 static gint
409 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
410 {
411         GdkEventButton *event_button = NULL;
412
413         if(widget == NULL || event == NULL || data == NULL) {
414                 return FALSE;
415         }
416
417         if(event->type == GDK_BUTTON_PRESS) {
418                 event_button = (GdkEventButton *) event;
419
420                 switch(event_button->button) {
421
422                 case 1:
423                         return byte_view_select(widget, event_button);
424                 case 3:
425                         return popup_menu_handler(widget, event, data);
426                 default:
427                         return FALSE;
428                 }
429         }
430
431         return FALSE;
432 }
433
434 GtkWidget *
435 create_byte_view(gint bv_size, GtkWidget *pane)
436 {
437   GtkWidget *byte_nb;
438
439   byte_nb = gtk_notebook_new();
440   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
441
442   gtk_paned_pack2(GTK_PANED(pane), byte_nb, FALSE, FALSE);
443   gtk_widget_set_usize(byte_nb, -1, bv_size);
444   gtk_widget_show(byte_nb);
445
446   /* Add a placeholder byte view so that there's at least something
447      displayed in the byte view notebook. */
448   add_byte_tab(byte_nb, "", NULL, NULL, NULL);
449
450   return byte_nb;
451 }
452
453 static void
454 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
455 {
456  const guint8 *byte_data;
457  guint byte_len;
458
459  byte_data = get_byte_view_data_and_length(bv, &byte_len);
460  if (byte_data == NULL) {
461    /* This must be the dummy byte view if no packet is selected. */
462    return;
463  }
464  packet_hex_print(GTK_TEXT(bv), byte_data, cfile.current_frame, NULL, byte_len);
465 }
466
467 static GtkWidget *
468 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
469     proto_tree *tree, GtkWidget *tree_view)
470 {
471   GtkWidget *byte_view, *byte_scrollw, *label;
472
473   /* Byte view.  Create a scrolled window for the text. */
474   byte_scrollw = scrolled_window_new(NULL, NULL);
475
476   /* Add scrolled pane to tabbed window */
477   label = gtk_label_new(name);
478   gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
479
480   /* The horizontal scrollbar of the scroll-window doesn't seem
481    * to affect the GtkText widget at all, even when line wrapping
482    * is turned off in the GtkText widget and there is indeed more
483    * horizontal data. */
484   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
485                         /* Horizontal */GTK_POLICY_NEVER,
486                         /* Vertical*/   GTK_POLICY_ALWAYS);
487   gtk_widget_show(byte_scrollw);
488
489   byte_view = gtk_text_new(NULL, NULL);
490   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
491   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
492   gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
493   gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY,
494                       (gpointer)tvb);
495   gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
496
497   gtk_signal_connect(GTK_OBJECT(byte_view), "show",
498                      GTK_SIGNAL_FUNC(byte_view_realize_cb), NULL);
499   gtk_signal_connect(GTK_OBJECT(byte_view), "button_press_event",
500                      GTK_SIGNAL_FUNC(byte_view_button_press_cb),
501                      gtk_object_get_data(GTK_OBJECT(popup_menu_object),
502                                          PM_HEXDUMP_KEY));
503
504   gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR,
505                       tree);
506   gtk_object_set_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR,
507                       tree_view);
508
509   gtk_widget_show(byte_view);
510
511   /* no tabs if this is the first page */
512   if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
513         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
514   else
515         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
516
517   return byte_view;
518 }
519
520 void
521 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
522     GtkWidget *byte_nb_ptr)
523 {
524         GSList *src_le;
525         data_source *src;
526
527         /*
528          * Get rid of all the old notebook tabs.
529          */
530         while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
531                 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
532
533         /*
534          * Add to the specified byte view notebook tabs for hex dumps
535          * of all the data sources for the specified frame.
536          */
537         for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
538                 src = src_le->data;
539                 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
540                     tree_view);
541         }
542
543         /*
544          * Initially select the first byte view.
545          */
546         gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
547 }
548
549 static void
550 packet_hex_print_common(GtkText *bv, const guint8 *pd, int len, int bstart,
551                         int bend, int encoding)
552 {
553   int      i = 0, j, k, cur;
554   guchar   line[MAX_LINE_LEN + 1];
555   static guchar hexchars[16] = {
556                 '0', '1', '2', '3', '4', '5', '6', '7',
557                 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
558   guchar   c = '\0';
559   unsigned int use_digits;
560   GdkFont *cur_font, *new_font;
561   GdkColor *fg, *bg;
562   gboolean reverse, newreverse;
563
564   /* Freeze the text for faster display */
565   gtk_text_freeze(bv);
566
567   /* Clear out the text */
568   gtk_text_set_point(bv, 0);
569   /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
570      http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
571      http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
572      for more information */
573   gtk_adjustment_set_value(bv->vadj, 0.0);
574   gtk_text_forward_delete(bv, gtk_text_get_length(bv));
575
576   /*
577    * How many of the leading digits of the offset will we supply?
578    * We always supply at least 4 digits, but if the maximum offset
579    * won't fit in 4 digits, we use as many digits as will be needed.
580    */
581   if (((len - 1) & 0xF0000000) != 0)
582     use_digits = 8;     /* need all 8 digits */
583   else if (((len - 1) & 0x0F000000) != 0)
584     use_digits = 7;     /* need 7 digits */
585   else if (((len - 1) & 0x00F00000) != 0)
586     use_digits = 6;     /* need 6 digits */
587   else if (((len - 1) & 0x000F0000) != 0)
588     use_digits = 5;     /* need 5 digits */
589   else
590     use_digits = 4;     /* we'll supply 4 digits */
591
592   /* Record the number of digits in this text view. */
593   gtk_object_set_data(GTK_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY,
594                       GUINT_TO_POINTER(use_digits));
595
596   while (i < len) {
597     /* Print the line number */
598     j = use_digits;
599     cur = 0;
600     do {
601       j--;
602       c = (i >> (j*4)) & 0xF;
603       line[cur++] = hexchars[c];
604     } while (j != 0);
605     line[cur++] = ' ';
606     line[cur++] = ' ';
607     line[cur] = '\0';
608
609     /* Display with inverse video ? */
610     if (prefs.gui_hex_dump_highlight_style) {
611       gtk_text_insert(bv, m_r_font, &BLACK, &WHITE, line, -1);
612       /* Do we start in reverse? */
613       reverse = i >= bstart && i < bend;
614       fg = reverse ? &WHITE : &BLACK;
615       bg = reverse ? &BLACK : &WHITE;
616       j   = i;
617       k   = i + BYTE_VIEW_WIDTH;
618       cur = 0;
619       /* Print the hex bit */
620       while (i < k) {
621         if (i < len) {
622           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
623           line[cur++] = hexchars[pd[i] & 0x0f];
624         } else {
625           line[cur++] = ' '; line[cur++] = ' ';
626         }
627         i++;
628         newreverse = i >= bstart && i < bend;
629         /* Have we gone from reverse to plain? */
630         if (reverse && (reverse != newreverse)) {
631           gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
632           fg = &BLACK;
633           bg = &WHITE;
634           cur = 0;
635         }
636         /* Inter byte space if not at end of line */
637         if (i < k) {
638           line[cur++] = ' ';
639           /* insert a space every BYTE_VIEW_SEP bytes */
640           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
641             line[cur++] = ' ';
642           }
643         }
644         /* Have we gone from plain to reversed? */
645         if (!reverse && (reverse != newreverse)) {
646           gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
647           fg = &WHITE;
648           bg = &BLACK;
649           cur = 0;
650         }
651         reverse = newreverse;
652       }
653       /* Print remaining part of line */
654       gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
655       cur = 0;
656       /* Print some space at the end of the line */
657       line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
658       gtk_text_insert(bv, m_r_font, &BLACK, &WHITE, line, cur);
659       cur = 0;
660
661       /* Print the ASCII bit */
662       i = j;
663       /* Do we start in reverse? */
664       reverse = i >= bstart && i < bend;
665       fg = reverse ? &WHITE : &BLACK;
666       bg = reverse ? &BLACK : &WHITE;
667       while (i < k) {
668         if (i < len) {
669           if (encoding == CHAR_ASCII) {
670             c = pd[i];
671           }
672           else if (encoding == CHAR_EBCDIC) {
673             c = EBCDIC_to_ASCII1(pd[i]);
674           }
675           else {
676                   g_assert_not_reached();
677           }
678           line[cur++] = isprint(c) ? c : '.';
679         } else {
680           line[cur++] = ' ';
681         }
682         i++;
683         newreverse = i >= bstart && i < bend;
684         /* Have we gone from reverse to plain? */
685         if (reverse && (reverse != newreverse)) {
686           gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
687           fg = &BLACK;
688           bg = &WHITE;
689           cur = 0;
690         }
691         if (i < k) {
692           /* insert a space every BYTE_VIEW_SEP bytes */
693           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
694             line[cur++] = ' ';
695           }
696         }
697         /* Have we gone from plain to reversed? */
698         if (!reverse && (reverse != newreverse)) {
699           gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
700           fg = &WHITE;
701           bg = &BLACK;
702           cur = 0;
703         }
704         reverse = newreverse;
705       }
706       /* Print remaining part of line */
707       gtk_text_insert(bv, m_r_font, fg, bg, line, cur);
708       cur = 0;
709       line[cur++] = '\n';
710       line[cur]   = '\0';
711       gtk_text_insert(bv, m_r_font, &BLACK, &WHITE, line, -1);
712     }
713     else {
714       gtk_text_insert(bv, m_r_font, NULL, NULL, line, -1);
715       /* Do we start in bold? */
716       cur_font = (i >= bstart && i < bend) ? m_b_font : m_r_font;
717       j   = i;
718       k   = i + BYTE_VIEW_WIDTH;
719       cur = 0;
720       /* Print the hex bit */
721       while (i < k) {
722         if (i < len) {
723           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
724           line[cur++] = hexchars[pd[i] & 0x0f];
725         } else {
726           line[cur++] = ' '; line[cur++] = ' ';
727         }
728         line[cur++] = ' ';
729         i++;
730         /* insert a space every BYTE_VIEW_SEP bytes */
731         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
732         /* Did we cross a bold/plain boundary? */
733         new_font = (i >= bstart && i < bend) ? m_b_font : m_r_font;
734         if (cur_font != new_font) {
735           gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
736           cur_font = new_font;
737           cur = 0;
738         }
739       }
740       line[cur++] = ' ';
741       gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
742
743       cur = 0;
744       i = j;
745       /* Print the ASCII bit */
746       cur_font = (i >= bstart && i < bend) ? m_b_font : m_r_font;
747       while (i < k) {
748         if (i < len) {
749           if (encoding == CHAR_ASCII) {
750             c = pd[i];
751           }
752           else if (encoding == CHAR_EBCDIC) {
753             c = EBCDIC_to_ASCII1(pd[i]);
754           }
755           else {
756                   g_assert_not_reached();
757           }
758           line[cur++] = isprint(c) ? c : '.';
759         } else {
760           line[cur++] = ' ';
761         }
762         i++;
763         /* insert a space every BYTE_VIEW_SEP bytes */
764         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
765         /* Did we cross a bold/plain boundary? */
766         new_font = (i >= bstart && i < bend) ? m_b_font : m_r_font;
767         if (cur_font != new_font) {
768           gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
769           cur_font = new_font;
770           cur = 0;
771         }
772       }
773       line[cur++] = '\n';
774       line[cur]   = '\0';
775       gtk_text_insert(bv, cur_font, NULL, NULL, line, -1);
776     }
777   }
778
779   /* scroll text into position */
780   gtk_text_thaw(bv); /* must thaw before adjusting scroll bars */
781   if ( bstart > 0 ) {
782     int linenum;
783     float scrollval;
784
785     linenum = bstart / BYTE_VIEW_WIDTH;
786     scrollval = MIN(linenum * m_font_height,
787                     bv->vadj->upper - bv->vadj->page_size);
788
789     gtk_adjustment_set_value(bv->vadj, scrollval);
790   }
791 }
792
793 void
794 packet_hex_print(GtkText *bv, const guint8 *pd, frame_data *fd,
795                  field_info *finfo, guint len)
796 {
797   /* do the initial printing and save the information needed    */
798   /* to redraw the display if preferences change.               */
799
800   int bstart, bend = -1, blen;
801
802   if (finfo != NULL) {
803     bstart = finfo->start;
804     blen = finfo->length;
805   } else {
806     bstart = -1;
807     blen = -1;
808   }
809   if (bstart >= 0 && blen >= 0) {
810     bend = bstart + blen;
811   }
812
813   /* save the information needed to redraw the text */
814   /* should we save the fd & finfo pointers instead ?? */
815   gtk_object_set_data(GTK_OBJECT(bv),  E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
816   gtk_object_set_data(GTK_OBJECT(bv),  E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
817   gtk_object_set_data(GTK_OBJECT(bv),  E_BYTE_VIEW_ENCODE_KEY, GINT_TO_POINTER(fd->flags.encoding));
818
819   packet_hex_print_common(bv, pd, len, bstart, bend, fd->flags.encoding);
820 }
821
822 /*
823  * Redraw the text using the saved information; usually called if
824  * the preferences have changed.
825  */
826 void
827 packet_hex_reprint(GtkText *bv)
828 {
829   int start, end, encoding;
830   const guint8 *data;
831   guint len;
832
833   start = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
834                           E_BYTE_VIEW_START_KEY));
835   end = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
836                         E_BYTE_VIEW_END_KEY));
837   data = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
838   g_assert(data != NULL);
839   encoding = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(bv),
840                              E_BYTE_VIEW_ENCODE_KEY));
841
842   packet_hex_print_common(bv, data, len, start, end, encoding);
843 }
844
845 /* List of all protocol tree widgets, so we can globally set the selection
846    mode and font of all of them. */
847 static GList *ptree_widgets;
848
849 /* Add a protocol tree widget to the list of protocol tree widgets. */
850 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
851
852 static void
853 remember_ptree_widget(GtkWidget *ptreew)
854 {
855   ptree_widgets = g_list_append(ptree_widgets, ptreew);
856
857   /* Catch the "destroy" event on the widget, so that we remove it from
858      the list when it's destroyed. */
859   gtk_signal_connect(GTK_OBJECT(ptreew), "destroy",
860                      GTK_SIGNAL_FUNC(forget_ptree_widget), NULL);
861 }
862
863 /* Remove a protocol tree widget from the list of protocol tree widgets. */
864 static void
865 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
866 {
867   ptree_widgets = g_list_remove(ptree_widgets, ptreew);
868 }
869
870 /* Set the selection mode of a given packet tree window. */
871 static void
872 set_ptree_sel_browse(GtkWidget *ptreew, gboolean val)
873 {
874         /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
875            I think "browse" in Ethereal makes more sense than "SINGLE" in
876            GTK+ */
877         if (val) {
878                 gtk_clist_set_selection_mode(GTK_CLIST(ptreew),
879                     GTK_SELECTION_SINGLE);
880         }
881         else {
882                 gtk_clist_set_selection_mode(GTK_CLIST(ptreew),
883                     GTK_SELECTION_BROWSE);
884         }
885 }
886
887 static void
888 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
889 {
890         set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
891 }
892
893 /* Set the selection mode of all packet tree windows. */
894 void
895 set_ptree_sel_browse_all(gboolean val)
896 {
897         g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
898 }
899
900 static void
901 set_ptree_style_cb(gpointer data, gpointer user_data)
902 {
903         gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
904 }
905
906 void
907 set_ptree_font_all(GdkFont *font)
908 {
909         GtkStyle *style;
910
911         style = gtk_style_new();
912         gdk_font_unref(style->font);
913         style->font = font;
914         gdk_font_ref(font);
915
916         g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
917
918         /* Now nuke the old style and replace it with the new one. */
919         gtk_style_unref(item_style);
920         item_style = style;
921 }
922
923 void
924 create_tree_view(gint tv_size, e_prefs *prefs, GtkWidget *pane,
925                 GtkWidget **tv_scrollw_p, GtkWidget **tree_view_p)
926 {
927   GtkWidget *tv_scrollw, *tree_view;
928
929   /* Tree view */
930   tv_scrollw = scrolled_window_new(NULL, NULL);
931   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
932     GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
933   gtk_paned_pack1(GTK_PANED(pane), tv_scrollw, TRUE, TRUE);
934   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
935   gtk_widget_show(tv_scrollw);
936
937   tree_view = ctree_new(1, 0);
938   gtk_signal_connect( GTK_OBJECT(tree_view), "key-press-event",
939                       (GtkSignalFunc) toggle_tree, NULL );
940   gtk_signal_connect( GTK_OBJECT(tree_view), "tree-expand",
941                       (GtkSignalFunc) expand_tree, NULL );
942   gtk_signal_connect( GTK_OBJECT(tree_view), "tree-collapse",
943                       (GtkSignalFunc) collapse_tree, NULL );
944   /* I need this next line to make the widget work correctly with hidden
945    * column titles and GTK_SELECTION_BROWSE */
946   gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
947   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
948   set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
949   gtk_widget_set_style(tree_view, item_style);
950   remember_ptree_widget(tree_view);
951
952   *tree_view_p = tree_view;
953   *tv_scrollw_p = tv_scrollw;
954 }
955
956 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view) {
957   int i;
958   for(i=0; i < num_tree_types; i++) {
959     tree_is_expanded[i] = TRUE;
960   }
961   proto_tree_draw(protocol_tree, tree_view);
962   gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
963 }
964
965 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view) {
966   int i;
967   for(i=0; i < num_tree_types; i++) {
968     tree_is_expanded[i] = FALSE;
969   }
970   proto_tree_draw(protocol_tree, tree_view);
971 }
972
973
974 struct proto_tree_draw_info {
975         GtkCTree        *ctree;
976         GtkCTreeNode    *ctree_node;
977 };
978
979 void
980 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
981 {
982         struct proto_tree_draw_info     info;
983
984         info.ctree = GTK_CTREE(tree_view);
985         info.ctree_node = NULL;
986
987         gtk_clist_freeze(GTK_CLIST(tree_view));
988
989         /*
990          * Clear out any crud left over in the display of the protocol
991          * tree, by removing all nodes from the ctree.
992          * This is how it's done in testgtk.c in GTK+.
993          */
994         gtk_clist_clear(GTK_CLIST(tree_view));
995
996         g_node_children_foreach((GNode*) protocol_tree, G_TRAVERSE_ALL,
997                 proto_tree_draw_node, &info);
998
999         gtk_clist_thaw(GTK_CLIST(tree_view));
1000 }
1001
1002 static void
1003 proto_tree_draw_node(GNode *node, gpointer data)
1004 {
1005         struct proto_tree_draw_info     info;
1006         struct proto_tree_draw_info     *parent_info = (struct proto_tree_draw_info*) data;
1007
1008         field_info      *fi = PITEM_FINFO(node);
1009         gchar           label_str[ITEM_LABEL_LENGTH];
1010         gchar           *label_ptr;
1011         GtkCTreeNode    *parent;
1012         gboolean        is_leaf, is_expanded;
1013
1014         if (!fi->visible)
1015                 return;
1016
1017         /* was a free format label produced? */
1018         if (fi->representation) {
1019                 label_ptr = fi->representation;
1020         }
1021         else { /* no, make a generic label */
1022                 label_ptr = label_str;
1023                 proto_item_fill_label(fi, label_str);
1024         }
1025
1026         if (g_node_n_children(node) > 0) {
1027                 is_leaf = FALSE;
1028                 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1029                 if (tree_is_expanded[fi->tree_type]) {
1030                         is_expanded = TRUE;
1031                 }
1032                 else {
1033                         is_expanded = FALSE;
1034                 }
1035         }
1036         else {
1037                 is_leaf = TRUE;
1038                 is_expanded = FALSE;
1039         }
1040
1041         info.ctree = parent_info->ctree;
1042         parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
1043                         &label_ptr, 5, NULL, NULL, NULL, NULL,
1044                         is_leaf, is_expanded );
1045
1046         gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
1047
1048         if (!is_leaf) {
1049                 info.ctree_node = parent;
1050                 g_node_children_foreach(node, G_TRAVERSE_ALL,
1051                         proto_tree_draw_node, &info);
1052         }
1053 }
1054
1055 /*
1056  * Clear the hex dump and protocol tree panes.
1057  */
1058 void
1059 clear_tree_and_hex_views(void)
1060 {
1061   /* Clear the hex dump by getting rid of all the byte views. */
1062   while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1063     gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1064
1065   /* Add a placeholder byte view so that there's at least something
1066      displayed in the byte view notebook. */
1067   add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1068
1069   /* Clear the protocol tree by removing all nodes in the ctree.
1070      This is how it's done in testgtk.c in GTK+ */
1071   gtk_clist_clear(GTK_CLIST(tree_view));
1072 }