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