Move the tap infrastructure to the epan directory.
[obnox/wireshark/wip.git] / gtk / proto_draw.c
1 /* proto_draw.c
2  * Routines for GTK+ packet display
3  *
4  * $Id$
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
34 #ifdef HAVE_IO_H
35 #include <io.h>                 /* open/close on win32 */
36 #endif
37
38 #ifdef HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif
41
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #include <gtk/gtk.h>
47 #include <gdk/gdkkeysyms.h>
48
49 #include <string.h>
50
51 #include <epan/epan_dissect.h>
52
53 #include "isprint.h"
54
55 #include "main.h"
56 #include <epan/packet.h>
57 #include <epan/charsets.h>
58 #include "menu.h"
59 #include "keys.h"
60
61 #include <epan/prefs.h>
62 #include "colors.h"
63 #include "file_dlg.h"
64 #include "proto_draw.h"
65 #include "packet_win.h"
66 #include "dlg_utils.h"
67 #include "ui_util.h"
68 #include "gtkglobals.h"
69 #include "compat_macros.h"
70 #include "alert_box.h"
71 #include "simple_dialog.h"
72 #include "progress_dlg.h"
73 #include "font_utils.h"
74
75
76 /* Win32 needs the O_BINARY flag for open() */
77 #ifndef O_BINARY
78 #define O_BINARY        0
79 #endif
80
81 #define BYTE_VIEW_WIDTH    16
82 #define BYTE_VIEW_SEP      8
83
84 #define E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
85 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
86 #define E_BYTE_VIEW_NDIGITS_KEY   "byte_view_ndigits"
87 #define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
88 #define E_BYTE_VIEW_START_KEY     "byte_view_start"
89 #define E_BYTE_VIEW_END_KEY       "byte_view_end"
90 #define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
91
92
93 #if GTK_MAJOR_VERSION < 2
94 GtkStyle *item_style = NULL;
95 #endif
96
97 /* gtk_tree_view_expand_to_path doesn't exist in gtk+ v2.0 so we must include it
98  * when building with this version (taken from gtk+ v2.2.4) */
99 #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION == 0
100 /**
101  * gtk_tree_view_expand_to_path:
102  * @tree_view: A #GtkTreeView.
103  * @path: path to a row.
104  *
105  * Expands the row at @path. This will also expand all parent rows of
106  * @path as necessary.
107  *
108  * Since: 2.2
109  **/
110 void
111 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
112                               GtkTreePath *path)
113 {
114   gint i, depth;
115   gint *indices;
116   GtkTreePath *tmp;
117
118   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
119   g_return_if_fail (path != NULL);
120
121   depth = gtk_tree_path_get_depth (path);
122   indices = gtk_tree_path_get_indices (path);
123
124   tmp = gtk_tree_path_new ();
125   g_return_if_fail (tmp != NULL);
126
127   for (i = 0; i < depth; i++)
128     {
129       gtk_tree_path_append_index (tmp, indices[i]);
130       gtk_tree_view_expand_row (tree_view, tmp, FALSE);
131     }
132
133   gtk_tree_path_free (tmp);
134 }
135 #endif
136
137 static GtkWidget *
138 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
139     proto_tree *tree, GtkWidget *tree_view);
140
141 static void
142 proto_tree_draw_node(proto_node *node, gpointer data);
143
144 /* Get the current text window for the notebook. */
145 GtkWidget *
146 get_notebook_bv_ptr(GtkWidget *nb_ptr)
147 {
148   int num;
149   GtkWidget *bv_page;
150
151   num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
152   bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
153   if (bv_page)
154       return GTK_BIN(bv_page)->child;
155   else
156       return NULL;
157 }
158
159 /*
160  * Get the data and length for a byte view, given the byte view page.
161  * Return the pointer, or NULL on error, and set "*data_len" to the length.
162  */
163 const guint8 *
164 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
165 {
166   tvbuff_t *byte_view_tvb;
167   const guint8 *data_ptr;
168
169   byte_view_tvb = OBJECT_GET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY);
170   if (byte_view_tvb == NULL)
171     return NULL;
172
173   data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
174   *data_len = tvb_length(byte_view_tvb);
175   return data_ptr;
176 }
177
178 /*
179  * Set the current text window for the notebook to the window that
180  * refers to a particular tvbuff.
181  */
182 void
183 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
184 {
185   int num;
186   GtkWidget *bv_page, *bv;
187   tvbuff_t *bv_tvb;
188
189   for (num = 0;
190        (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
191        num++) {
192     bv = GTK_BIN(bv_page)->child;
193     bv_tvb = OBJECT_GET_DATA(bv, E_BYTE_VIEW_TVBUFF_KEY);
194     if (bv_tvb == tvb) {
195       /* Found it. */
196       gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
197       break;
198     }
199   }
200 }
201
202 /* Redraw a given byte view window. */
203 void
204 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
205 {
206   GtkWidget *bv;
207   const guint8 *data;
208   guint len;
209
210   bv = get_notebook_bv_ptr(nb);
211   if (bv != NULL) {
212     data = get_byte_view_data_and_length(bv, &len);
213     if (data != NULL)
214 #if GTK_MAJOR_VERSION < 2
215       packet_hex_print(GTK_TEXT(bv), data, fd, finfo, len);
216 #else
217       packet_hex_print(GTK_TEXT_VIEW(bv), data, fd, finfo, len);
218 #endif
219   }
220 }
221
222 /* Redraw all byte view windows. */
223 void
224 redraw_hex_dump_all(void)
225 {
226     if (cfile.current_frame != NULL)
227             redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
228
229   redraw_hex_dump_packet_wins();
230
231 #if GTK_MAJOR_VERSION >= 2
232   /* XXX - this is a hack, to workaround a bug in GTK2.x!
233      when changing the font size, even refilling of the corresponding 
234      gtk_text_buffer doesn't seem to trigger an update.     
235      The only workaround is to freshly select the frame, which will remove any 
236      existing notebook tabs and "restart" the whole byte view again. */
237   if (cfile.current_frame != NULL)
238     goto_frame(&cfile, cfile.current_frame->num);
239 #endif
240 }
241
242 #if GTK_MAJOR_VERSION < 2
243 static void
244 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
245 #else
246 static void
247 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
248             GtkTreePath *path _U_, gpointer user_data _U_)
249 #endif
250 {
251     field_info   *finfo;
252 #if GTK_MAJOR_VERSION >= 2
253     GtkTreeModel *model;
254
255     model = gtk_tree_view_get_model(tree_view);
256     gtk_tree_model_get(model, iter, 1, &finfo, -1);
257 #else
258     finfo = gtk_ctree_node_get_row_data( ctree, node);
259 #endif
260     g_assert(finfo);
261
262     /*
263      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
264      * are thus presumably leaf nodes and cannot be expanded.
265      */
266     if (finfo->tree_type != -1) {
267         g_assert(finfo->tree_type >= 0 &&
268                  finfo->tree_type < num_tree_types);
269         tree_is_expanded[finfo->tree_type] = TRUE;
270     }
271 }
272
273 #if GTK_MAJOR_VERSION < 2
274 static void
275 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
276 #else
277 static void
278 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
279             GtkTreePath *path _U_, gpointer user_data _U_)
280 #endif
281 {
282     field_info   *finfo;
283 #if GTK_MAJOR_VERSION >= 2
284     GtkTreeModel *model;
285
286     model = gtk_tree_view_get_model(tree_view);
287     gtk_tree_model_get(model, iter, 1, &finfo, -1);
288 #else
289     finfo = gtk_ctree_node_get_row_data( ctree, node);
290 #endif
291     g_assert(finfo);
292
293     /*
294      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
295      * are thus presumably leaf nodes and cannot be collapsed.
296      */
297     if (finfo->tree_type != -1) {
298         g_assert(finfo->tree_type >= 0 &&
299                  finfo->tree_type < num_tree_types);
300         tree_is_expanded[finfo->tree_type] = FALSE;
301     }
302 }
303
304 #if GTK_MAJOR_VERSION < 2
305 static void
306 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
307 {
308         if (event->keyval != GDK_Return)
309                 return;
310         gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
311 }
312 #endif
313
314 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
315 #define BYTES_PER_LINE  16      /* max byte values in a line */
316 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3 + 1)
317                                 /* max number of characters hex dump takes -
318                                    2 digits plus trailing blank
319                                    plus separator between first and
320                                    second 8 digits */
321 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
322                                 /* number of characters those bytes take;
323                                    3 characters per byte of hex dump,
324                                    2 blanks separating hex from ASCII,
325                                    1 character per byte of ASCII dump */
326 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
327                                 /* number of characters per line;
328                                    offset, 2 blanks separating offset
329                                    from data dump, data dump */
330
331 /* Which byte the offset is referring to. Associates
332  * whitespace with the preceding digits. */
333 static int
334 byte_num(int offset, int start_point)
335 {
336         return (offset - start_point) / 3;
337 }
338
339 #if GTK_MAJOR_VERSION >= 2
340 struct field_lookup_info {
341     field_info  *fi;
342     GtkTreeIter  iter;
343 };
344
345 static gboolean
346 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
347             gpointer data)
348 {
349     field_info *fi;
350     struct field_lookup_info *fli = (struct field_lookup_info *)data;
351
352     gtk_tree_model_get(model, iter, 1, &fi, -1);
353     if (fi == fli->fi) {
354         fli->iter = *iter;
355         return TRUE;
356     }
357     return FALSE;
358 }
359
360 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
361   GtkTreeModel *model;
362   struct field_lookup_info fli;
363
364   model = gtk_tree_view_get_model(tree_view);
365   fli.fi = finfo;
366   gtk_tree_model_foreach(model, lookup_finfo, &fli);
367
368   return gtk_tree_model_get_path(model, &fli.iter);
369 }
370
371 #endif
372
373 /* If the user selected a certain byte in the byte view, try to find
374  * the item in the GUI proto_tree that corresponds to that byte, and:
375  *
376  *      if we succeed, select it, and return TRUE;
377  *      if we fail, return FALSE. */
378 gboolean
379 byte_view_select(GtkWidget *widget, GdkEventButton *event)
380 {
381     proto_tree   *tree;
382 #if GTK_MAJOR_VERSION < 2
383     GtkCTree     *ctree;
384     GtkCTreeNode *node, *parent;
385     GtkText      *bv = GTK_TEXT(widget);
386 #else
387     GtkTreeView  *tree_view;
388     GtkTreeModel *model;
389     GtkTreePath  *first_path, *path;
390     GtkTreeIter   parent;
391     GtkTextView  *bv = GTK_TEXT_VIEW(widget);
392     gint          x, y;
393     GtkTextIter   iter;
394     struct field_lookup_info fli;
395 #endif
396     field_info   *finfo;
397     int           row, column;
398     int           byte;
399     tvbuff_t     *tvb;
400     guint         ndigits;
401     int           digits_start_1;
402     int           digits_end_1;
403     int           digits_start_2;
404     int           digits_end_2;
405     int           text_start_1;
406     int           text_end_1;
407     int           text_start_2;
408     int           text_end_2;
409
410     /*
411      * Get the number of digits of offset being displayed, and
412      * compute the columns of various parts of the display.
413      */
414     ndigits = GPOINTER_TO_UINT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY));
415
416     /*
417      * The column of the first hex digit in the first half.
418      * That starts after "ndigits" digits of offset and two
419      * separating blanks.
420      */
421     digits_start_1 = ndigits + 2;
422
423     /*
424      * The column of the last hex digit in the first half.
425      * There are BYTES_PER_LINE/2 bytes displayed in the first
426      * half; there are 2 characters per byte, plus a separating
427      * blank after all but the last byte's characters.
428      *
429      * Then subtract 1 to get the last column of the first half
430      * rather than the first column after the first half.
431      */
432     digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
433         (BYTES_PER_LINE/2 - 1) - 1;
434
435     /*
436      * The column of the first hex digit in the second half.
437      * Add back the 1 to get the first column after the first
438      * half, and then add 2 for the 2 separating blanks between
439      * the halves.
440      */
441     digits_start_2 = digits_end_1 + 3;
442
443     /*
444      * The column of the last hex digit in the second half.
445      * Add the same value we used to get "digits_end_1" from
446      * "digits_start_1".
447      */
448     digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
449         (BYTES_PER_LINE/2 - 1) - 1;
450
451     /*
452      * The column of the first "text dump" character in the first half.
453      * Add back the 1 to get the first column after the second
454      * half's hex dump, and then add 3 for the 3 separating blanks
455      * between the hex and text dummp.
456      */
457     text_start_1 = digits_end_2 + 4;
458
459     /*
460      * The column of the last "text dump" character in the first half.
461      * There are BYTES_PER_LINE/2 bytes displayed in the first
462      * half; there is 1 character per byte.
463      *
464      * Then subtract 1 to get the last column of the first half
465      * rather than the first column after the first half.
466      */
467     text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
468
469     /*
470      * The column of the first "text dump" character in the second half.
471      * Add back the 1 to get the first column after the first half,
472      * and then add 1 for the separating blank between the halves.
473      */
474     text_start_2 = text_end_1 + 2;
475
476     /*
477      * The column of the last "text dump" character in second half.
478      * Add the same value we used to get "text_end_1" from
479      * "text_start_1".
480      */
481     text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
482
483     tree = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_PTR);
484     if (tree == NULL) {
485         /*
486          * Somebody clicked on the dummy byte view; do nothing.
487          */
488         return FALSE;
489     }
490 #if GTK_MAJOR_VERSION < 2
491     ctree = GTK_CTREE(OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_VIEW_PTR));
492 #else
493     tree_view = GTK_TREE_VIEW(OBJECT_GET_DATA(widget,
494                                               E_BYTE_VIEW_TREE_VIEW_PTR));
495 #endif
496
497 #if GTK_MAJOR_VERSION < 2
498     /* Given the mouse (x,y) and the current GtkText (h,v)
499      * adjustments, and the size of the font, figure out
500      * which text column/row the user selected. This could be off
501      * if the bold version of the font is bigger than the
502      * regular version of the font. */
503     column = (int) ((bv->hadj->value + event->x) / user_font_get_regular_width());
504     row = (int) ((bv->vadj->value + event->y) / user_font_get_regular_height());
505 #else
506     /* get the row/column selected */
507     gtk_text_view_window_to_buffer_coords(bv,
508                          gtk_text_view_get_window_type(bv, event->window),
509                          (gint) event->x, (gint) event->y, &x, &y);
510     gtk_text_view_get_iter_at_location(bv, &iter, x, y);
511     row = gtk_text_iter_get_line(&iter);
512     column = gtk_text_iter_get_line_offset(&iter);
513 #endif
514
515     /* Given the column and row, determine which byte offset
516      * the user clicked on. */
517     if (column >= digits_start_1 && column <= digits_end_1) {
518         byte = byte_num(column, digits_start_1);
519         if (byte == -1) {
520             return FALSE;
521         }
522     }
523     else if (column >= digits_start_2 && column <= digits_end_2) {
524         byte = byte_num(column, digits_start_2);
525         if (byte == -1) {
526             return FALSE;
527         }
528         byte += 8;
529     }
530     else if (column >= text_start_1 && column <= text_end_1) {
531         byte = column - text_start_1;
532     }
533     else if (column >= text_start_2 && column <= text_end_2) {
534         byte = 8 + column - text_start_2;
535     }
536     else {
537         /* The user didn't select a hex digit or
538          * text-dump character. */
539         return FALSE;
540     }
541
542     /* Add the number of bytes from the previous rows. */
543     byte += row * 16;
544
545     /* Get the data source tvbuff */
546     tvb = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TVBUFF_KEY);
547
548     /* Find the finfo that corresponds to our byte. */
549     finfo = proto_find_field_from_offset(tree, byte, tvb);
550
551     if (!finfo) {
552         return FALSE;
553     }
554
555 #if GTK_MAJOR_VERSION < 2
556     node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
557     g_assert(node);
558
559     /* Expand and select our field's row */
560     gtk_ctree_expand(ctree, node);
561     gtk_ctree_select(ctree, node);
562     expand_tree(ctree, node, NULL);
563
564     /* ... and its parents */
565     parent = GTK_CTREE_ROW(node)->parent;
566     while (parent) {
567         gtk_ctree_expand(ctree, parent);
568         expand_tree(ctree, parent, NULL);
569         parent = GTK_CTREE_ROW(parent)->parent;
570     }
571
572     /* And position the window so the selection is visible.
573      * Position the selection in the middle of the viewable
574      * pane. */
575     gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
576
577     return FALSE;
578 #else
579     model = gtk_tree_view_get_model(tree_view);
580     fli.fi = finfo;
581     gtk_tree_model_foreach(model, lookup_finfo, &fli);
582
583     /* Expand our field's row */
584     first_path = gtk_tree_model_get_path(model, &fli.iter);
585     gtk_tree_view_expand_row(tree_view, first_path, FALSE);
586     expand_tree(tree_view, &fli.iter, NULL, NULL);
587
588     /* ... and its parents */
589     while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
590         path = gtk_tree_model_get_path(model, &parent);
591         gtk_tree_view_expand_row(tree_view, path, FALSE);
592         expand_tree(tree_view, &parent, NULL, NULL);
593         fli.iter = parent;
594         gtk_tree_path_free(path);
595     }
596
597     /* select our field's row */
598     gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
599                                    first_path);
600
601     /* And position the window so the selection is visible.
602      * Position the selection in the middle of the viewable
603      * pane. */
604     gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
605
606     gtk_tree_path_free(first_path);
607
608     return TRUE;
609 #endif
610 }
611
612 /* Calls functions for different mouse-button presses. */
613 static gint
614 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
615 {
616         GdkEventButton *event_button = NULL;
617
618         if(widget == NULL || event == NULL || data == NULL) {
619                 return FALSE;
620         }
621
622         if(event->type == GDK_BUTTON_PRESS) {
623                 event_button = (GdkEventButton *) event;
624
625         /* To qoute the "Gdk Event Structures" doc:
626          * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
627                 switch(event_button->button) {
628
629                 case 1:
630                         return byte_view_select(widget, event_button);
631                 case 3:
632                         return popup_menu_handler(widget, event, data);
633                 default:
634                         return FALSE;
635                 }
636         }
637
638         return FALSE;
639 }
640
641 GtkWidget *
642 byte_view_new(void)
643 {
644   GtkWidget *byte_nb;
645
646   byte_nb = gtk_notebook_new();
647   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
648
649   /* this will only have an effect, if no tabs are shown */
650   gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
651
652   /* set the tabs scrollable, if they don't fit into the pane */
653   gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
654
655   /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
656   gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
657
658   /* Add a placeholder byte view so that there's at least something
659      displayed in the byte view notebook. */
660   add_byte_tab(byte_nb, "", NULL, NULL, NULL);
661
662   return byte_nb;
663 }
664
665 static void
666 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
667 {
668     const guint8 *byte_data;
669     guint byte_len;
670
671     byte_data = get_byte_view_data_and_length(bv, &byte_len);
672     if (byte_data == NULL) {
673         /* This must be the dummy byte view if no packet is selected. */
674         return;
675     }
676 #if GTK_MAJOR_VERSION < 2
677     packet_hex_print(GTK_TEXT(bv), byte_data, cfile.current_frame, NULL,
678                      byte_len);
679 #else
680     packet_hex_print(GTK_TEXT_VIEW(bv), byte_data, cfile.current_frame, NULL,
681                      byte_len);
682 #endif
683 }
684
685 static GtkWidget *
686 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
687              proto_tree *tree, GtkWidget *tree_view)
688 {
689   GtkWidget *byte_view, *byte_scrollw, *label;
690 #if GTK_MAJOR_VERSION >= 2
691   GtkTextBuffer *buf;
692   GtkStyle      *style;
693 #endif
694
695   /* Byte view.  Create a scrolled window for the text. */
696   byte_scrollw = scrolled_window_new(NULL, NULL);
697 #if GTK_MAJOR_VERSION < 2
698   /* The horizontal scrollbar of the scroll-window doesn't seem
699    * to affect the GtkText widget at all, even when line wrapping
700    * is turned off in the GtkText widget and there is indeed more
701    * horizontal data. */
702   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
703                         /* Horizontal */GTK_POLICY_NEVER,
704                         /* Vertical*/   GTK_POLICY_ALWAYS);
705 #else
706   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw), 
707                                    GTK_SHADOW_IN);
708 #endif
709   /* Add scrolled pane to tabbed window */
710   label = gtk_label_new(name);
711   gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
712
713   gtk_widget_show(byte_scrollw);
714
715 #if GTK_MAJOR_VERSION < 2
716   byte_view = gtk_text_new(NULL, NULL);
717   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
718   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
719   gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
720 #else
721   byte_view = gtk_text_view_new();
722   gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
723   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
724   buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
725   style = gtk_widget_get_style(GTK_WIDGET(byte_view));
726   gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
727   gtk_text_buffer_create_tag(buf, "reverse",
728                              "font-desc", user_font_get_regular(),
729                              "foreground-gdk", &style->text[GTK_STATE_SELECTED],
730                              "background-gdk", &style->base[GTK_STATE_SELECTED],
731                              NULL);
732   gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
733 #endif
734   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, tvb);
735   gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
736
737   SIGNAL_CONNECT(byte_view, "show", byte_view_realize_cb, NULL);
738   SIGNAL_CONNECT(byte_view, "button_press_event", byte_view_button_press_cb,
739                  OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
740
741   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, tree);
742   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
743
744   gtk_widget_show(byte_view);
745
746   /* no tabs if this is the first page */
747   if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
748         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
749   else
750         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
751
752   /* set this page (this will print the packet data) */
753   gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb), 
754     gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
755
756   return byte_view;
757 }
758
759 void
760 add_main_byte_views(epan_dissect_t *edt)
761 {
762     add_byte_views(edt, tree_view, byte_nb_ptr);
763 }
764
765 void
766 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
767                GtkWidget *byte_nb_ptr)
768 {
769         GSList *src_le;
770         data_source *src;
771
772         /*
773          * Get rid of all the old notebook tabs.
774          */
775         while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
776                 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
777
778         /*
779          * Add to the specified byte view notebook tabs for hex dumps
780          * of all the data sources for the specified frame.
781          */
782         for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
783                 src = src_le->data;
784                 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
785                              tree_view);
786         }
787
788         /*
789          * Initially select the first byte view.
790          */
791         gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
792 }
793
794
795
796 static GtkWidget *savehex_dlg=NULL;
797
798 static void
799 savehex_dlg_destroy_cb(void)
800 {
801         savehex_dlg = NULL;
802 }
803
804 extern void
805 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_)  
806 {
807         GtkWidget *bv;
808         int len;
809         int i=0;
810         const guint8 *data_p = NULL;
811         GString *ASCII_representation = g_string_new("");
812         GString *byte_str = g_string_new("");
813         
814         bv = get_notebook_bv_ptr(byte_nb_ptr);
815         if (bv == NULL) {
816                 /* shouldn't happen */ 
817                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
818                 return;
819         }
820         
821         data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
822         
823         g_string_sprintfa(byte_str,"%04x  ",i); /* Offset 0000 */
824         for (i=0; i<len; i++){
825           g_string_sprintfa(ASCII_representation,"%c",isprint(*data_p) ? *data_p : '.');
826           g_string_sprintfa(byte_str," %02x",*data_p++);
827           if ((i+1)%16==0 && i!=0){
828             g_string_sprintfa(byte_str,"  %s\n%04x  ",ASCII_representation->str,i+1);
829             g_string_assign (ASCII_representation,"");
830           }
831         }
832         
833         if(ASCII_representation->len){
834           for (i=ASCII_representation->len; i<16; i++){
835             g_string_sprintfa(byte_str,"   ");
836           }
837           g_string_sprintfa(byte_str,"  %s\n",ASCII_representation->str);
838         }
839         /* Now that we have the byte data, copy it into the default clipboard */
840         copy_to_clipboard(byte_str);
841         g_string_free(byte_str, TRUE);                       /* Free the memory */
842         g_string_free(ASCII_representation, TRUE);           /* Free the memory */
843 }
844
845 /* save the current highlighted hex data */
846 static void
847 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
848 {
849         GtkWidget *bv;
850         int fd, start, end, len;
851         const guint8 *data_p = NULL;
852         const char *file = NULL;
853
854 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
855     file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
856 #else
857     file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(savehex_dlg));
858 #endif
859
860         if (!file ||! *file) {
861                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
862                 return;
863         }
864
865         /* Must check if file name exists first */
866
867         bv = get_notebook_bv_ptr(byte_nb_ptr);
868         if (bv == NULL) {
869                 /* shouldn't happen */
870                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
871                 return;
872         }
873         /*
874          * Retrieve the info we need 
875          */
876         end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
877         start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
878         data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
879
880         if (data_p == NULL || start == -1 || start > end) {
881                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
882                     "No data selected to save!");
883                 return;
884         }
885
886         fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
887         if (fd == -1) {
888                 open_failure_alert_box(file, errno, TRUE);
889                 return;
890         }
891         if (write(fd, data_p + start, end - start) < 0) {
892                 write_failure_alert_box(file, errno);
893                 close(fd);
894                 return;
895         }
896         if (close(fd) < 0) {
897                 write_failure_alert_box(file, errno);
898                 return;
899         }
900
901         /* Get rid of the dialog box */
902         window_destroy(GTK_WIDGET(savehex_dlg));
903 }
904
905 /* Launch the dialog box to put up the file selection box etc */
906 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
907 {
908         int start, end, len;
909         const guint8 *data_p = NULL;
910     gchar *label;
911
912     GtkWidget   *bv;
913         GtkWidget   *dlg_lb;
914
915
916     /* don't show up the dialog, if no data has to be saved */
917         bv = get_notebook_bv_ptr(byte_nb_ptr);
918         if (bv == NULL) {
919                 /* shouldn't happen */
920                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
921                 return;
922         }
923         end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
924         start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
925         data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
926
927         if (data_p == NULL || start == -1 || start > end) {
928                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
929                 return;
930         }
931
932     /* if the window is already open, bring it to front */
933         if(savehex_dlg){
934                 reactivate_window(savehex_dlg);
935                 return;
936         }
937
938         /*
939          * Build the dialog box we need.
940          */
941     savehex_dlg = file_selection_new("Ethereal: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
942
943     /* label */
944     label = g_strdup_printf("Will save %u %s of raw binary data to specified file.", 
945         end - start, plurality(end - start, "byte", "bytes"));
946     dlg_lb = gtk_label_new(label);
947     g_free(label);
948     file_selection_set_extra_widget(savehex_dlg, dlg_lb);
949         gtk_widget_show(dlg_lb);
950
951     SIGNAL_CONNECT(savehex_dlg, "destroy", savehex_dlg_destroy_cb, NULL);
952
953 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
954     if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
955         savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
956     } else {
957         window_destroy(savehex_dlg);
958     }
959 #else
960     /* Connect the ok_button to file_save_as_ok_cb function and pass along a
961     pointer to the file selection box widget */
962     SIGNAL_CONNECT(GTK_FILE_SELECTION (savehex_dlg)->ok_button, "clicked",
963         savehex_save_clicked_cb, savehex_dlg);
964
965     window_set_cancel_button(savehex_dlg, 
966     GTK_FILE_SELECTION(savehex_dlg)->cancel_button, window_cancel_button_cb);
967
968     SIGNAL_CONNECT(savehex_dlg, "delete_event", window_delete_event_cb, NULL);
969
970     gtk_file_selection_set_filename(GTK_FILE_SELECTION(savehex_dlg), "");
971
972     gtk_widget_show_all(savehex_dlg);
973     window_present(savehex_dlg);
974 #endif
975 }
976
977
978
979 /* Update the progress bar this many times when reading a file. */
980 #define N_PROGBAR_UPDATES       100
981
982
983 /*
984  * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
985  * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
986  * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3.  (That's
987  * presumably why there's a progress bar for it.)
988  *
989  * Perhaps what's needed is a custom widget (either one that lets you stuff
990  * text into it more quickly, or one that's a "virtual" widget so that the
991  * text for a row is constructed, via a callback, when the row is to be
992  * displayed).  A custom widget might also let us treat the offset, hex
993  * data, and ASCII data as three columns, so you can select purely in
994  * the hex dump column.
995  */
996 #if GTK_MAJOR_VERSION < 2
997 static void
998 packet_hex_print_common(GtkText *bv, const guint8 *pd, int len, int bstart,
999                         int bend, int encoding)
1000 #else
1001 static void
1002 packet_hex_print_common(GtkTextView *bv, const guint8 *pd, int len, int bstart,
1003                         int bend, int encoding)
1004 #endif
1005 {
1006   int            i = 0, j, k, cur;
1007   guchar         line[MAX_LINE_LEN + 1];
1008   static guchar  hexchars[16] = {
1009       '0', '1', '2', '3', '4', '5', '6', '7',
1010       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1011   guchar         c = '\0';
1012   unsigned int   use_digits;
1013   gboolean       reverse, newreverse;
1014 #if GTK_MAJOR_VERSION < 2
1015   GdkFont       *cur_font, *new_font;
1016   GdkColor      *fg, *bg;
1017 #else
1018   GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bv));
1019   GtkTextIter    iter;
1020   char          *revstyle;
1021   gchar         *convline;
1022   gsize          newsize;
1023   GtkTextMark   *mark = NULL;
1024 #endif
1025
1026   progdlg_t  *progbar = NULL;
1027   float       progbar_val;
1028   gboolean    progbar_stop_flag;
1029   GTimeVal    progbar_start_time;
1030   gchar       progbar_status_str[100];
1031   int         progbar_nextstep;
1032   int         progbar_quantum;
1033
1034 #if GTK_MAJOR_VERSION < 2
1035   /* Freeze the text for faster display */
1036   gtk_text_freeze(bv);
1037
1038   /* Clear out the text */
1039   gtk_text_set_point(bv, 0);
1040   /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
1041      http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
1042      http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
1043      for more information */
1044   gtk_adjustment_set_value(bv->vadj, 0.0);
1045   gtk_text_forward_delete(bv, gtk_text_get_length(bv));
1046 #else
1047   gtk_text_buffer_set_text(buf, "", 0);
1048   gtk_text_buffer_get_start_iter(buf, &iter);
1049 #endif
1050
1051   /*
1052    * How many of the leading digits of the offset will we supply?
1053    * We always supply at least 4 digits, but if the maximum offset
1054    * won't fit in 4 digits, we use as many digits as will be needed.
1055    */
1056   if (((len - 1) & 0xF0000000) != 0)
1057     use_digits = 8;     /* need all 8 digits */
1058   else if (((len - 1) & 0x0F000000) != 0)
1059     use_digits = 7;     /* need 7 digits */
1060   else if (((len - 1) & 0x00F00000) != 0)
1061     use_digits = 6;     /* need 6 digits */
1062   else if (((len - 1) & 0x000F0000) != 0)
1063     use_digits = 5;     /* need 5 digits */
1064   else
1065     use_digits = 4;     /* we'll supply 4 digits */
1066
1067   /* Record the number of digits in this text view. */
1068   OBJECT_SET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1069
1070   /* Update the progress bar when it gets to this value. */
1071   progbar_nextstep = 0;
1072   /* When we reach the value that triggers a progress bar update,
1073      bump that value by this amount. */
1074   progbar_quantum = len/N_PROGBAR_UPDATES;
1075
1076   progbar_stop_flag = FALSE;
1077   g_get_current_time(&progbar_start_time);
1078
1079   while (i < len) {
1080     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1081        when we update it, we have to run the GTK+ main loop to get it
1082        to repaint what's pending, and doing so may involve an "ioctl()"
1083        to see if there's any pending input from an X server, and doing
1084        that for every packet can be costly, especially on a big file. */
1085     if (i >= progbar_nextstep) {
1086       /* let's not divide by zero. I should never be started
1087        * with count == 0, so let's assert that
1088        */
1089       g_assert(len > 0);
1090       progbar_val = (gfloat) i / len;
1091
1092       if (progbar == NULL)
1093         /* Create the progress bar if necessary */
1094         progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1095                                               &progbar_stop_flag,
1096                                               &progbar_start_time,
1097                                               progbar_val);
1098
1099       if (progbar != NULL) {
1100         g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1101                    "%4u of %u bytes", i, len);
1102         update_progress_dlg(progbar, progbar_val, progbar_status_str);
1103       }
1104
1105       progbar_nextstep += progbar_quantum;
1106     }
1107
1108     if (progbar_stop_flag) {
1109       /* Well, the user decided to abort the operation.  Just stop,
1110          and arrange to return TRUE to our caller, so they know it
1111          was stopped explicitly. */
1112       break;
1113     }
1114
1115     /* Print the line number */
1116     j = use_digits;
1117     cur = 0;
1118     do {
1119       j--;
1120       c = (i >> (j*4)) & 0xF;
1121       line[cur++] = hexchars[c];
1122     } while (j != 0);
1123     line[cur++] = ' ';
1124     line[cur++] = ' ';
1125     line[cur] = '\0';
1126
1127     /* Display with inverse video ? */
1128 #if GTK_MAJOR_VERSION < 2
1129     if (prefs.gui_hex_dump_highlight_style) {
1130       gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1131       /* Do we start in reverse? */
1132       reverse = i >= bstart && i < bend;
1133       fg = reverse ? &WHITE : &BLACK;
1134       bg = reverse ? &BLACK : &WHITE;
1135       j   = i;
1136       k   = i + BYTE_VIEW_WIDTH;
1137       cur = 0;
1138       /* Print the hex bit */
1139       while (i < k) {
1140         if (i < len) {
1141           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1142           line[cur++] = hexchars[pd[i] & 0x0f];
1143         } else {
1144           line[cur++] = ' '; line[cur++] = ' ';
1145         }
1146         i++;
1147         newreverse = i >= bstart && i < bend;
1148         /* Have we gone from reverse to plain? */
1149         if (reverse && (reverse != newreverse)) {
1150           gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1151           fg = &BLACK;
1152           bg = &WHITE;
1153           cur = 0;
1154         }
1155         /* Inter byte space if not at end of line */
1156         if (i < k) {
1157           line[cur++] = ' ';
1158           /* insert a space every BYTE_VIEW_SEP bytes */
1159           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1160             line[cur++] = ' ';
1161           }
1162         }
1163         /* Have we gone from plain to reversed? */
1164         if (!reverse && (reverse != newreverse)) {
1165           gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1166           fg = &WHITE;
1167           bg = &BLACK;
1168           cur = 0;
1169         }
1170         reverse = newreverse;
1171       }
1172       /* Print remaining part of line */
1173       gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1174       cur = 0;
1175       /* Print some space at the end of the line */
1176       line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1177       gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, cur);
1178       cur = 0;
1179
1180       /* Print the ASCII bit */
1181       i = j;
1182       /* Do we start in reverse? */
1183       reverse = i >= bstart && i < bend;
1184       fg = reverse ? &WHITE : &BLACK;
1185       bg = reverse ? &BLACK : &WHITE;
1186       while (i < k) {
1187         if (i < len) {
1188           if (encoding == CHAR_ASCII) {
1189             c = pd[i];
1190           }
1191           else if (encoding == CHAR_EBCDIC) {
1192             c = EBCDIC_to_ASCII1(pd[i]);
1193           }
1194           else {
1195                   g_assert_not_reached();
1196           }
1197           line[cur++] = isprint(c) ? c : '.';
1198         } else {
1199           line[cur++] = ' ';
1200         }
1201         i++;
1202         newreverse = i >= bstart && i < bend;
1203         /* Have we gone from reverse to plain? */
1204         if (reverse && (reverse != newreverse)) {
1205           gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1206           fg = &BLACK;
1207           bg = &WHITE;
1208           cur = 0;
1209         }
1210         if (i < k) {
1211           /* insert a space every BYTE_VIEW_SEP bytes */
1212           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1213             line[cur++] = ' ';
1214           }
1215         }
1216         /* Have we gone from plain to reversed? */
1217         if (!reverse && (reverse != newreverse)) {
1218           gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1219           fg = &WHITE;
1220           bg = &BLACK;
1221           cur = 0;
1222         }
1223         reverse = newreverse;
1224       }
1225       /* Print remaining part of line */
1226       gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1227       cur = 0;
1228       line[cur++] = '\n';
1229       line[cur]   = '\0';
1230       gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1231     }
1232     else {
1233       gtk_text_insert(bv, user_font_get_regular(), NULL, NULL, line, -1);
1234       /* Do we start in bold? */
1235       cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1236       j   = i;
1237       k   = i + BYTE_VIEW_WIDTH;
1238       cur = 0;
1239       /* Print the hex bit */
1240       while (i < k) {
1241         if (i < len) {
1242           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1243           line[cur++] = hexchars[pd[i] & 0x0f];
1244         } else {
1245           line[cur++] = ' '; line[cur++] = ' ';
1246         }
1247         line[cur++] = ' ';
1248         i++;
1249         /* insert a space every BYTE_VIEW_SEP bytes */
1250         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1251         /* Did we cross a bold/plain boundary? */
1252         new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1253         if (cur_font != new_font) {
1254           gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1255           cur_font = new_font;
1256           cur = 0;
1257         }
1258       }
1259       line[cur++] = ' ';
1260       gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1261
1262       cur = 0;
1263       i = j;
1264       /* Print the ASCII bit */
1265       cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1266       while (i < k) {
1267         if (i < len) {
1268           if (encoding == CHAR_ASCII) {
1269             c = pd[i];
1270           }
1271           else if (encoding == CHAR_EBCDIC) {
1272             c = EBCDIC_to_ASCII1(pd[i]);
1273           }
1274           else {
1275                   g_assert_not_reached();
1276           }
1277           line[cur++] = isprint(c) ? c : '.';
1278         } else {
1279           line[cur++] = ' ';
1280         }
1281         i++;
1282         /* insert a space every BYTE_VIEW_SEP bytes */
1283         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1284         /* Did we cross a bold/plain boundary? */
1285         new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1286         if (cur_font != new_font) {
1287           gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1288           cur_font = new_font;
1289           cur = 0;
1290         }
1291       }
1292       line[cur++] = '\n';
1293       line[cur]   = '\0';
1294       gtk_text_insert(bv, cur_font, NULL, NULL, line, -1);
1295     }
1296 #else
1297     if (prefs.gui_hex_dump_highlight_style)
1298       revstyle = "reverse";
1299     else
1300       revstyle = "bold";
1301
1302     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1, "plain",
1303                                              NULL);
1304     /* Do we start in reverse? */
1305     reverse = i >= bstart && i < bend;
1306     j   = i;
1307     k   = i + BYTE_VIEW_WIDTH;
1308     cur = 0;
1309     /* Print the hex bit */
1310     while (i < k) {
1311       if (i < len) {
1312         line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1313         line[cur++] = hexchars[pd[i] & 0x0f];
1314       } else {
1315         line[cur++] = ' '; line[cur++] = ' ';
1316       }
1317       i++;
1318       newreverse = i >= bstart && i < bend;
1319       /* Have we gone from reverse to plain? */
1320       if (reverse && (reverse != newreverse)) {
1321         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1322                                                  revstyle, NULL);
1323         cur = 0;
1324       }
1325       /* Inter byte space if not at end of line */
1326       if (i < k) {
1327         line[cur++] = ' ';
1328         /* insert a space every BYTE_VIEW_SEP bytes */
1329         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1330           line[cur++] = ' ';
1331         }
1332       }
1333       /* Have we gone from plain to reversed? */
1334       if (!reverse && (reverse != newreverse)) {
1335         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1336                                                  "plain", NULL);
1337         mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1338         cur = 0;
1339       }
1340       reverse = newreverse;
1341     }
1342     /* Print remaining part of line */
1343     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1344                                              reverse ? revstyle : "plain",
1345                                              NULL);
1346     cur = 0;
1347     /* Print some space at the end of the line */
1348     line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1349     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1350                                              "plain", NULL);
1351     cur = 0;
1352
1353     /* Print the ASCII bit */
1354     i = j;
1355     /* Do we start in reverse? */
1356     reverse = i >= bstart && i < bend;
1357     while (i < k) {
1358       if (i < len) {
1359         if (encoding == CHAR_ASCII) {
1360           c = pd[i];
1361         }
1362         else if (encoding == CHAR_EBCDIC) {
1363           c = EBCDIC_to_ASCII1(pd[i]);
1364         }
1365         else {
1366           g_assert_not_reached();
1367         }
1368         line[cur++] = isprint(c) ? c : '.';
1369       } else {
1370         line[cur++] = ' ';
1371       }
1372       i++;
1373       newreverse = i >= bstart && i < bend;
1374       /* Have we gone from reverse to plain? */
1375       if (reverse && (reverse != newreverse)) {
1376         convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1377         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1378                                                  revstyle, NULL);
1379         g_free(convline);
1380         cur = 0;
1381       }
1382       if (i < k) {
1383         /* insert a space every BYTE_VIEW_SEP bytes */
1384         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1385           line[cur++] = ' ';
1386         }
1387       }
1388       /* Have we gone from plain to reversed? */
1389       if (!reverse && (reverse != newreverse)) {
1390         convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1391         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1392                                                  "plain", NULL);
1393         g_free(convline);
1394         cur = 0;
1395       }
1396       reverse = newreverse;
1397     }
1398     /* Print remaining part of line */
1399     convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1400     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1401                                              reverse ? revstyle : "plain",
1402                                              NULL);
1403     g_free(convline);
1404     cur = 0;
1405     line[cur++] = '\n';
1406     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1407                                              "plain", NULL);
1408 #endif
1409   }
1410
1411   /* We're done printing the packets; destroy the progress bar if
1412      it was created. */
1413   if (progbar != NULL)
1414     destroy_progress_dlg(progbar);
1415
1416   /* scroll text into position */
1417 #if GTK_MAJOR_VERSION < 2
1418   gtk_text_thaw(bv); /* must thaw before adjusting scroll bars */
1419   if ( bstart > 0 ) {
1420     int linenum;
1421     float scrollval;
1422
1423     linenum = bstart / BYTE_VIEW_WIDTH;
1424     scrollval = MIN(linenum * user_font_get_regular_height(),
1425                     bv->vadj->upper - bv->vadj->page_size);
1426
1427     gtk_adjustment_set_value(bv->vadj, scrollval);
1428   }
1429 #else
1430   if (mark) {
1431       gtk_text_view_scroll_to_mark(bv, mark, 0.0, TRUE, 1.0, 0.0);
1432       gtk_text_buffer_delete_mark(buf, mark);
1433   }
1434 #endif
1435 }
1436
1437 #if GTK_MAJOR_VERSION < 2
1438 void
1439 packet_hex_print(GtkText *bv, const guint8 *pd, frame_data *fd,
1440                  field_info *finfo, guint len)
1441 #else
1442 void
1443 packet_hex_print(GtkTextView *bv, const guint8 *pd, frame_data *fd,
1444                  field_info *finfo, guint len)
1445 #endif
1446 {
1447   /* do the initial printing and save the information needed    */
1448   /* to redraw the display if preferences change.               */
1449
1450   int bstart, bend = -1, blen;
1451
1452   if (finfo != NULL) {
1453     bstart = finfo->start;
1454     blen = finfo->length;
1455   } else {
1456     bstart = -1;
1457     blen = -1;
1458   }
1459   if (bstart >= 0 && blen >= 0) {
1460     bend = bstart + blen;
1461   }
1462
1463   /* save the information needed to redraw the text */
1464   /* should we save the fd & finfo pointers instead ?? */
1465   OBJECT_SET_DATA(bv, E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1466   OBJECT_SET_DATA(bv, E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1467   OBJECT_SET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY,
1468                   GINT_TO_POINTER(fd->flags.encoding));
1469
1470   packet_hex_print_common(bv, pd, len, bstart, bend, fd->flags.encoding);
1471 }
1472
1473 /*
1474  * Redraw the text using the saved information; usually called if
1475  * the preferences have changed.
1476  */
1477 #if GTK_MAJOR_VERSION < 2
1478 void
1479 packet_hex_reprint(GtkText *bv)
1480 #else
1481 void
1482 packet_hex_reprint(GtkTextView *bv)
1483 #endif
1484 {
1485   int start, end, encoding;
1486   const guint8 *data;
1487   guint len;
1488
1489   start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1490   end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1491   data = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
1492   g_assert(data != NULL);
1493   encoding = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY));
1494
1495   packet_hex_print_common(bv, data, len, start, end, encoding);
1496 }
1497
1498 /* List of all protocol tree widgets, so we can globally set the selection
1499    mode and font of all of them. */
1500 static GList *ptree_widgets;
1501
1502 /* Add a protocol tree widget to the list of protocol tree widgets. */
1503 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1504
1505 static void
1506 remember_ptree_widget(GtkWidget *ptreew)
1507 {
1508   ptree_widgets = g_list_append(ptree_widgets, ptreew);
1509
1510   /* Catch the "destroy" event on the widget, so that we remove it from
1511      the list when it's destroyed. */
1512   SIGNAL_CONNECT(ptreew, "destroy", forget_ptree_widget, NULL);
1513 }
1514
1515 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1516 static void
1517 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1518 {
1519   ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1520 }
1521
1522 /* Set the selection mode of a given packet tree window. */
1523 static void
1524 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1525 {
1526 #if GTK_MAJOR_VERSION >= 2
1527     GtkTreeSelection *selection;
1528
1529     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1530 #endif
1531     /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1532        I think "browse" in Ethereal makes more sense than "SINGLE" in
1533        GTK+ */
1534     if (val) {
1535 #if GTK_MAJOR_VERSION < 2
1536         gtk_clist_set_selection_mode(GTK_CLIST(tree),
1537                                      GTK_SELECTION_SINGLE);
1538 #else
1539         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1540 #endif
1541     }
1542     else {
1543 #if GTK_MAJOR_VERSION < 2
1544         gtk_clist_set_selection_mode(GTK_CLIST(tree),
1545                                      GTK_SELECTION_BROWSE);
1546 #else
1547         gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1548 #endif
1549     }
1550 }
1551
1552 static void
1553 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1554 {
1555         set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1556 }
1557
1558 /* Set the selection mode of all packet tree windows. */
1559 void
1560 set_ptree_sel_browse_all(gboolean val)
1561 {
1562         g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1563 }
1564
1565 #if GTK_MAJOR_VERSION < 2
1566 static void
1567 set_ptree_style_cb(gpointer data, gpointer user_data)
1568 {
1569         gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
1570 }
1571 #else
1572 static void
1573 set_ptree_font_cb(gpointer data, gpointer user_data)
1574 {
1575         gtk_widget_modify_font((GtkWidget *)data,
1576                                (PangoFontDescription *)user_data);
1577 }
1578 #endif
1579
1580 void
1581 set_ptree_font_all(FONT_TYPE *font)
1582 {
1583 #if GTK_MAJOR_VERSION < 2
1584     GtkStyle *style;
1585
1586     style = gtk_style_new();
1587     gdk_font_unref(style->font);
1588     style->font = font;
1589     gdk_font_ref(font);
1590
1591     g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
1592
1593     /* Now nuke the old style and replace it with the new one. */
1594     gtk_style_unref(item_style);
1595     item_style = style;
1596 #else
1597     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1598 #endif
1599 }
1600
1601
1602 #if GTK_MAJOR_VERSION >= 2
1603 void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1604                                              GtkCellRenderer *cell,
1605                                              GtkTreeModel *tree_model,
1606                                              GtkTreeIter *iter,
1607                                              gpointer data _U_)
1608 {
1609     field_info   *fi;
1610
1611     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1612
1613     /* for each field, we have to reset the renderer attributes */
1614     g_object_set (cell, "foreground-set", FALSE, NULL);
1615
1616     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1617     g_object_set (cell, "underline-set", FALSE, NULL);
1618
1619     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1620     g_object_set (cell, "style-set", FALSE, NULL);*/
1621
1622     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1623     g_object_set (cell, "weight-set", FALSE, NULL);*/
1624
1625     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1626         /* as some fonts don't support italic, don't use this */
1627         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1628         g_object_set (cell, "style-set", TRUE, NULL);
1629         */
1630         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1631         g_object_set (cell, "weight-set", TRUE, NULL);*/
1632     }
1633
1634     if(fi->hfinfo->type == FT_FRAMENUM) {
1635         g_object_set (cell, "foreground", "blue", NULL);
1636         g_object_set (cell, "foreground-set", TRUE, NULL);
1637
1638         g_object_set (cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1639         g_object_set (cell, "underline-set", TRUE, NULL);
1640     }
1641 }
1642 #endif
1643
1644
1645 GtkWidget *
1646 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1647 {
1648   GtkWidget *tv_scrollw, *tree_view;
1649 #if GTK_MAJOR_VERSION >= 2
1650   GtkTreeStore *store;
1651   GtkCellRenderer *renderer;
1652   GtkTreeViewColumn *column;
1653   gint col_offset;
1654 #endif
1655
1656   /* Tree view */
1657   tv_scrollw = scrolled_window_new(NULL, NULL);
1658 #if GTK_MAJOR_VERSION >= 2
1659   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw), 
1660                                    GTK_SHADOW_IN);
1661 #endif
1662
1663 #if GTK_MAJOR_VERSION < 2
1664   tree_view = ctree_new(1, 0);
1665   SIGNAL_CONNECT(tree_view, "key-press-event", toggle_tree, NULL );
1666   SIGNAL_CONNECT(tree_view, "tree-expand", expand_tree, NULL );
1667   SIGNAL_CONNECT(tree_view, "tree-collapse", collapse_tree, NULL );
1668   /* I need this next line to make the widget work correctly with hidden
1669    * column titles and GTK_SELECTION_BROWSE */
1670   gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1671 #else
1672   store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1673   tree_view = tree_view_new(GTK_TREE_MODEL(store));
1674   g_object_unref(G_OBJECT(store));
1675   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1676   renderer = gtk_cell_renderer_text_new();
1677   g_object_set (renderer, "ypad", 0, NULL);
1678   col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1679                                                            -1, "Name", renderer,
1680                                                            "text", 0, NULL);
1681   column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1682                                     col_offset - 1);
1683   gtk_tree_view_column_set_cell_data_func(column,
1684                                              renderer,
1685                                              tree_cell_renderer,
1686                                              NULL,
1687                                              NULL);
1688
1689   gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1690                                   GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1691   SIGNAL_CONNECT(tree_view, "row-expanded", expand_tree, NULL);
1692   SIGNAL_CONNECT(tree_view, "row-collapsed", collapse_tree, NULL);
1693 #endif
1694   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1695   set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1696 #if GTK_MAJOR_VERSION < 2
1697   if(item_style == NULL) {
1698       item_style = gtk_style_new();
1699       gdk_font_unref(item_style->font);
1700       item_style->font = user_font_get_regular();
1701   }
1702
1703   gtk_widget_set_style(tree_view, item_style);
1704 #else
1705   gtk_widget_modify_font(tree_view, user_font_get_regular());
1706 #endif
1707   remember_ptree_widget(tree_view);
1708
1709   *tree_view_p = tree_view;
1710
1711   return tv_scrollw;
1712 }
1713
1714 #if GTK_MAJOR_VERSION < 2
1715 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1716 #else
1717 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1718 #endif
1719 {
1720   int i;
1721   for(i=0; i < num_tree_types; i++) {
1722     tree_is_expanded[i] = TRUE;
1723   }
1724 #if GTK_MAJOR_VERSION < 2
1725   proto_tree_draw(protocol_tree, tree_view);
1726   gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
1727 #else
1728   gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1729 #endif
1730 }
1731
1732 #if GTK_MAJOR_VERSION < 2
1733 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1734 #else
1735 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1736 #endif
1737 {
1738   int i;
1739   for(i=0; i < num_tree_types; i++) {
1740     tree_is_expanded[i] = FALSE;
1741   }
1742 #if GTK_MAJOR_VERSION < 2
1743   proto_tree_draw(protocol_tree, tree_view);
1744 #else
1745   gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1746 #endif
1747 }
1748
1749
1750 struct proto_tree_draw_info {
1751 #if GTK_MAJOR_VERSION < 2
1752     GtkCTree     *ctree;
1753     GtkCTreeNode *ctree_node;
1754 #else
1755     GtkTreeView  *tree_view;
1756     GtkTreeIter  *iter;
1757 #endif
1758 };
1759
1760 void
1761 main_proto_tree_draw(proto_tree *protocol_tree)
1762 {
1763     proto_tree_draw(protocol_tree, tree_view);
1764 }
1765
1766
1767 void
1768 tree_view_follow_link(field_info   *fi)
1769 {
1770     if(fi->hfinfo->type == FT_FRAMENUM) {
1771         goto_frame(&cfile, fi->value.value.integer);
1772     }
1773 }
1774
1775
1776 /* If the user selected a position in the tree view, try to find
1777  * the item in the GUI proto_tree that corresponds to that byte, and
1778  * select it. */
1779 gboolean
1780 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1781 {
1782 #if GTK_MAJOR_VERSION < 2
1783         GtkCTree     *ctree;
1784         GtkCTreeNode *node;
1785         gint         row;
1786         gint         column;
1787         field_info   *fi;
1788
1789
1790         if(gtk_clist_get_selection_info(GTK_CLIST(widget), 
1791             (gint) (((GdkEventButton *)event)->x),
1792             (gint) (((GdkEventButton *)event)->y),
1793             &row, &column))
1794         {
1795             ctree = GTK_CTREE(widget);
1796
1797             node = gtk_ctree_node_nth(ctree, row);
1798             g_assert(node);
1799
1800             gtk_ctree_select(ctree, node);
1801
1802             /* if that's a doubleclick, try to follow the link */
1803             if(event->type == GDK_2BUTTON_PRESS) {
1804                 fi = gtk_ctree_node_get_row_data(ctree, node);
1805                 tree_view_follow_link(fi);
1806             }
1807         } else {
1808             return FALSE;
1809         }
1810 #else
1811         GtkTreeSelection    *sel;
1812         GtkTreePath         *path;
1813
1814         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1815                                           (gint) (((GdkEventButton *)event)->x),
1816                                           (gint) (((GdkEventButton *)event)->y),
1817                                           &path, NULL, NULL, NULL))
1818         {
1819             sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1820             gtk_tree_selection_select_path(sel, path);
1821
1822             /* if that's a doubleclick, try to follow the link */
1823             if(event->type == GDK_2BUTTON_PRESS) {
1824                 GtkTreeModel *model;
1825                 GtkTreeIter iter;
1826                 field_info   *fi;
1827
1828                 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1829                     gtk_tree_model_get(model, &iter, 1, &fi, -1);
1830                     tree_view_follow_link(fi);
1831                 }
1832             }
1833         } else {
1834             return FALSE;
1835         }
1836 #endif
1837     return TRUE;
1838 }
1839
1840 void
1841 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1842 {
1843 #if GTK_MAJOR_VERSION >= 2
1844     GtkTreeStore *store;
1845 #endif
1846     struct proto_tree_draw_info info;
1847
1848 #if GTK_MAJOR_VERSION < 2
1849     info.ctree = GTK_CTREE(tree_view);
1850     info.ctree_node = NULL;
1851
1852     gtk_clist_freeze(GTK_CLIST(tree_view));
1853 #else
1854     info.tree_view = GTK_TREE_VIEW(tree_view);
1855     info.iter = NULL;
1856
1857     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1858 #endif
1859
1860     /*
1861      * Clear out any crud left over in the display of the protocol
1862      * tree, by removing all nodes from the tree.
1863      * This is how it's done in testgtk.c in GTK+.
1864      */
1865 #if GTK_MAJOR_VERSION < 2
1866     gtk_clist_clear(GTK_CLIST(tree_view));
1867 #else
1868     gtk_tree_store_clear(store);
1869 #endif
1870
1871     proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1872
1873 #if GTK_MAJOR_VERSION < 2
1874     gtk_clist_thaw(GTK_CLIST(tree_view));
1875 #endif
1876 }
1877
1878 static void
1879 proto_tree_draw_node(proto_node *node, gpointer data)
1880 {
1881     struct proto_tree_draw_info info;
1882     struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1883
1884     field_info   *fi = PITEM_FINFO(node);
1885     gchar         label_str[ITEM_LABEL_LENGTH];
1886     gchar        *label_ptr;
1887     gboolean      is_leaf, is_expanded;
1888 #if GTK_MAJOR_VERSION < 2
1889     GtkCTreeNode *parent;
1890 #else
1891     GtkTreeStore *store;
1892     GtkTreeIter   iter;
1893     GtkTreePath  *path;
1894 #endif
1895
1896     if (PROTO_ITEM_IS_HIDDEN(node))
1897         return;
1898
1899     /* was a free format label produced? */
1900     if (fi->rep) {
1901         label_ptr = fi->rep->representation;
1902     }
1903     else { /* no, make a generic label */
1904         label_ptr = label_str;
1905         proto_item_fill_label(fi, label_str);
1906     }
1907
1908     if (node->first_child != NULL) {
1909         is_leaf = FALSE;
1910         g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1911         if (tree_is_expanded[fi->tree_type]) {
1912             is_expanded = TRUE;
1913         }
1914         else {
1915             is_expanded = FALSE;
1916         }
1917     }
1918     else {
1919         is_leaf = TRUE;
1920         is_expanded = FALSE;
1921     }
1922
1923     if(PROTO_ITEM_IS_GENERATED(node)) {
1924         label_ptr = g_strdup_printf("[%s]", label_ptr);
1925     }
1926
1927 #if GTK_MAJOR_VERSION < 2
1928     info.ctree = parent_info->ctree;
1929     parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
1930                                      &label_ptr, 5, NULL, NULL, NULL, NULL,
1931                                      is_leaf, is_expanded );
1932
1933     gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
1934 #else
1935     info.tree_view = parent_info->tree_view;
1936     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1937     gtk_tree_store_append(store, &iter, parent_info->iter);
1938     gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1939 #endif
1940
1941     if(PROTO_ITEM_IS_GENERATED(node)) {
1942         g_free(label_ptr);
1943     }
1944
1945     if (!is_leaf) {
1946 #if GTK_MAJOR_VERSION < 2
1947         info.ctree_node = parent;
1948 #else
1949         info.iter = &iter;
1950 #endif
1951         proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1952 #if GTK_MAJOR_VERSION >= 2
1953         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1954         if (is_expanded)
1955 /* #if GTK_MINOR_VERSION >= 2 */
1956             gtk_tree_view_expand_to_path(info.tree_view, path);
1957 /*#else
1958             gtk_tree_view_expand_row(info.tree_view, path, FALSE);
1959 #endif*/
1960         else
1961             gtk_tree_view_collapse_row(info.tree_view, path);
1962         gtk_tree_path_free(path);
1963 #endif
1964     }
1965 }
1966
1967 /*
1968  * Clear the hex dump and protocol tree panes.
1969  */
1970 void
1971 clear_tree_and_hex_views(void)
1972 {
1973   /* Clear the hex dump by getting rid of all the byte views. */
1974   while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1975     gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1976
1977   /* Add a placeholder byte view so that there's at least something
1978      displayed in the byte view notebook. */
1979   add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1980
1981   /* Clear the protocol tree by removing all nodes in the ctree.
1982      This is how it's done in testgtk.c in GTK+ */
1983 #if GTK_MAJOR_VERSION < 2
1984   gtk_clist_clear(GTK_CLIST(tree_view));
1985 #else
1986   gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));
1987 #endif
1988 }
1989