Instead of using private #defines for the last argument to
[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 E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
83 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
84 #define E_BYTE_VIEW_NDIGITS_KEY   "byte_view_ndigits"
85 #define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
86 #define E_BYTE_VIEW_START_KEY     "byte_view_start"
87 #define E_BYTE_VIEW_END_KEY       "byte_view_end"
88 #define E_BYTE_VIEW_MASK_KEY      "byte_view_mask"
89 #define E_BYTE_VIEW_MASKLE_KEY    "byte_view_mask_le"
90 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
91 #define E_BYTE_VIEW_APP_END_KEY   "byte_view_app_end"
92 #define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
93
94 static GtkWidget *
95 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
96              proto_tree *tree, GtkWidget *tree_view);
97
98 static void
99 proto_tree_draw_node(proto_node *node, gpointer data);
100
101 /* Get the current text window for the notebook. */
102 GtkWidget *
103 get_notebook_bv_ptr(GtkWidget *nb_ptr)
104 {
105   int num;
106   GtkWidget *bv_page;
107
108   num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
109   bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
110   if (bv_page)
111       return GTK_BIN(bv_page)->child;
112   else
113       return NULL;
114 }
115
116 /*
117  * Get the data and length for a byte view, given the byte view page.
118  * Return the pointer, or NULL on error, and set "*data_len" to the length.
119  */
120 const guint8 *
121 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
122 {
123   tvbuff_t *byte_view_tvb;
124   const guint8 *data_ptr;
125
126   byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
127   if (byte_view_tvb == NULL)
128     return NULL;
129
130   data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
131   *data_len = tvb_length(byte_view_tvb);
132   return data_ptr;
133 }
134
135 /*
136  * Set the current text window for the notebook to the window that
137  * refers to a particular tvbuff.
138  */
139 void
140 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
141 {
142   int num;
143   GtkWidget *bv_page, *bv;
144   tvbuff_t *bv_tvb;
145
146   for (num = 0;
147        (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
148        num++) {
149     bv = GTK_BIN(bv_page)->child;
150     bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
151     if (bv_tvb == tvb) {
152       /* Found it. */
153       gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
154       break;
155     }
156   }
157 }
158
159 /* Redraw a given byte view window. */
160 void
161 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
162 {
163   GtkWidget *bv;
164   const guint8 *data;
165   guint len;
166
167   bv = get_notebook_bv_ptr(nb);
168   if (bv != NULL) {
169     data = get_byte_view_data_and_length(bv, &len);
170     if (data != NULL)
171       packet_hex_print(bv, data, fd, finfo, len);
172   }
173 }
174
175 /* Redraw all byte view windows. */
176 void
177 redraw_packet_bytes_all(void)
178 {
179     if (cfile.current_frame != NULL)
180         redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
181
182     redraw_packet_bytes_packet_wins();
183
184     /* XXX - this is a hack, to workaround a bug in GTK2.x!
185        when changing the font size, even refilling of the corresponding
186        gtk_text_buffer doesn't seem to trigger an update.
187        The only workaround is to freshly select the frame, which will remove any
188        existing notebook tabs and "restart" the whole byte view again. */
189     if (cfile.current_frame != NULL) {
190         cfile.current_row = -1;
191         cf_goto_frame(&cfile, cfile.current_frame->num);
192     }
193 }
194
195 static void
196 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
197             GtkTreePath *path _U_, gpointer user_data _U_)
198 {
199     field_info   *finfo;
200     GtkTreeModel *model;
201
202     model = gtk_tree_view_get_model(tree_view);
203     gtk_tree_model_get(model, iter, 1, &finfo, -1);
204     g_assert(finfo);
205
206     /*
207      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
208      * are thus presumably leaf nodes and cannot be expanded.
209      */
210     if (finfo->tree_type != -1) {
211         g_assert(finfo->tree_type >= 0 &&
212                  finfo->tree_type < num_tree_types);
213         tree_is_expanded[finfo->tree_type] = TRUE;
214     }
215 }
216
217 static void
218 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
219               GtkTreePath *path _U_, gpointer user_data _U_)
220 {
221     field_info   *finfo;
222     GtkTreeModel *model;
223
224     model = gtk_tree_view_get_model(tree_view);
225     gtk_tree_model_get(model, iter, 1, &finfo, -1);
226     g_assert(finfo);
227
228     /*
229      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
230      * are thus presumably leaf nodes and cannot be collapsed.
231      */
232     if (finfo->tree_type != -1) {
233         g_assert(finfo->tree_type >= 0 &&
234                  finfo->tree_type < num_tree_types);
235         tree_is_expanded[finfo->tree_type] = FALSE;
236     }
237 }
238
239 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
240 #define BYTES_PER_LINE  16      /* max byte values in a line */
241 #define BITS_PER_LINE   8       /* max bit values in a line */
242 #define BYTE_VIEW_SEP   8       /* insert a space every BYTE_VIEW_SEP bytes */
243 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3 + 1)
244                                 /* max number of characters hex dump takes -
245                                    2 digits plus trailing blank
246                                    plus separator between first and
247                                    second 8 digits */
248 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
249                                 /* number of characters those bytes take;
250                                    3 characters per byte of hex dump,
251                                    2 blanks separating hex from ASCII,
252                                    1 character per byte of ASCII dump */
253 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
254                                 /* number of characters per line;
255                                    offset, 2 blanks separating offset
256                                    from data dump, data dump */
257 #define MAX_LINES       100
258 #define MAX_LINES_LEN   (MAX_LINES*MAX_LINE_LEN)
259
260 /* Which byte the offset is referring to. Associates
261  * whitespace with the preceding digits. */
262 static int
263 byte_num(int offset, int start_point)
264 {
265         return (offset - start_point) / 3;
266 }
267 static int
268 bit_num(int offset, int start_point)
269 {
270         return (offset - start_point) / 9;
271 }
272
273 struct field_lookup_info {
274     field_info  *fi;
275     GtkTreeIter  iter;
276 };
277
278 static gboolean
279 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
280              gpointer data)
281 {
282     field_info *fi;
283     struct field_lookup_info *fli = (struct field_lookup_info *)data;
284
285     gtk_tree_model_get(model, iter, 1, &fi, -1);
286     if (fi == fli->fi) {
287         fli->iter = *iter;
288         return TRUE;
289     }
290     return FALSE;
291 }
292
293 GtkTreePath
294 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
295 {
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 = -1;
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_gbl, byte_nb_ptr_gbl);
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
764 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
765 {
766     const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
767     int i, j;
768     gboolean end_of_line = TRUE; /* Initial state is end of line */
769     int byte_line_part_length;
770
771     GString* hex_str;
772     GString* char_str;
773
774     /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
775     hex_str = g_string_new("");
776     char_str= g_string_new("");
777
778     i = 0;
779     while (i<data_len) {
780         if(end_of_line) {
781             g_string_append_printf(hex_str,"%04x  ",i); /* Offset - note that we _append_ here */
782         }
783
784         g_string_append_printf(hex_str," %02x",*data_p);
785         if(append_text) {
786             g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
787         }
788
789         ++data_p;
790
791         /* Look ahead to see if this is the end of the data */
792         byte_line_part_length = (++i) % byte_line_length;
793         if(i == data_len){
794             /* End of data - need to fill in spaces in hex string and then do "end of line".
795              *
796              */
797             for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
798                 g_string_append(hex_str,"   "); /* Three spaces for each missing byte */
799             }
800             end_of_line = TRUE;
801         } else {
802             end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
803         }
804
805
806         if (end_of_line){
807             /* End of line */
808             g_string_append(copy_buffer, hex_str->str);
809             if(append_text) {
810                 /* Two spaces between hex and text */
811                 g_string_append_c(copy_buffer, ' ');
812                 g_string_append_c(copy_buffer, ' ');
813                 g_string_append(copy_buffer, char_str->str);
814             }
815             /* Setup ready for next line */
816             g_string_assign(char_str,"");
817             g_string_assign(hex_str, "\n");
818         }
819     }
820
821     g_string_free(hex_str, TRUE);
822     g_string_free(char_str, TRUE);
823 }
824
825 static int
826 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
827 {
828
829     gchar to_append;
830
831     /* Copy printable characters, newlines, and (horizontal) tabs. */
832     if(isprint(*data_p)) {
833         to_append = *data_p;
834     } else if(*data_p==0x0a) {
835         to_append = '\n';
836     } else if(*data_p==0x09) {
837         to_append = '\t';
838     } else {
839         return 1; /* Just ignore non-printable bytes */
840     }
841     g_string_append_c(copy_buffer,to_append);
842     return 1;
843 }
844
845 static
846 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
847 {
848     g_string_append_printf(copy_buffer, "%02x", *data_p);
849     return 1;
850 }
851
852 void
853 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
854 {
855     GtkWidget *bv;
856
857     guint len = 0;
858     int bytes_consumed = 0;
859     int flags;
860
861     const guint8* data_p;
862
863     GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
864
865     bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
866     if (bv == NULL) {
867             /* shouldn't happen */
868             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
869             return;
870     }
871
872     data_p = get_byte_view_data_and_length(bv, &len);
873     g_assert(data_p != NULL);
874
875     flags = data_type & CD_FLAGSMASK;
876     data_type = data_type & CD_TYPEMASK;
877
878     if(flags & CD_FLAGS_SELECTEDONLY) {
879         int start, end;
880
881         /* Get the start and end of the highlighted bytes. */
882         start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
883         end = 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_gbl);
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         start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
975         end = 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
1013 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1014 {
1015         int start, end;
1016         guint len;
1017         const guint8 *data_p = NULL;
1018         gchar *label;
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_gbl);
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         start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1035         end = 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 static GtkTextMark *
1097 packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int start, int end, guint32 mask, int mask_le, int use_digits, int create_mark)
1098 {
1099         GtkTextIter i_start, i_stop, iter;
1100
1101         GtkTextTag    *revstyle_tag;
1102         const char    *revstyle;
1103
1104         int per_line = 0;
1105         int per_one = 0;
1106         int bits_per_one = 0;
1107         int hex_offset, ascii_offset;
1108
1109         int start_line, start_line_pos;
1110         int stop_line, stop_line_pos;
1111
1112         if (start == -1 || end == -1)
1113                 return NULL;
1114
1115         /* Display with inverse video ? */
1116         if (prefs.gui_hex_dump_highlight_style)
1117                 revstyle = "reverse";
1118         else
1119                 revstyle = "bold";
1120
1121         revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle);
1122
1123         switch (recent.gui_bytes_view) {
1124         case BYTES_HEX:
1125                 per_line = BYTES_PER_LINE;
1126                 per_one  = 2+1; /* "ff " */
1127                 bits_per_one = 4;
1128                 break;
1129         case BYTES_BITS:
1130                 per_line = BITS_PER_LINE;
1131                 per_one  = 8+1; /* "10101010 " */
1132                 bits_per_one = 1;
1133                 break;
1134         default:
1135                 g_assert_not_reached();
1136         }
1137
1138         start_line = start / per_line;
1139         start_line_pos = start % per_line;
1140
1141         stop_line = end / per_line;
1142         stop_line_pos = end % per_line;
1143
1144 #define hex_fix(pos)   hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1145 #define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1146
1147         hex_offset = use_digits + 2;
1148         ascii_offset = hex_fix(per_line) + 2;
1149
1150         gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos));
1151
1152         /* stig: it should be done only for bitview... */
1153         if (mask == 0x00 || recent.gui_bytes_view != BYTES_BITS) {
1154                 while (start_line <= stop_line) {
1155                         int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1156
1157                         if (start_line_pos == line_pos_end) break;
1158
1159                         /* bits/hex */
1160                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
1161                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1);
1162                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1163
1164                         /* ascii */
1165                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
1166                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end));
1167                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1168
1169                         start_line_pos = 0;
1170                         start_line++;
1171                 }
1172
1173         } else if (mask_le) { /* LSB of mask first (little-endian) */
1174                 while (start_line <= stop_line) {
1175                         int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1176                         int line_pos = start_line_pos;
1177
1178                         while (line_pos < line_pos_end) {
1179                                 int lop = 8 / bits_per_one;
1180                                 int mask_per_one = (1 << bits_per_one) - 1;
1181                                 int ascii_on = 0;
1182
1183                                 while (lop--) {
1184                                         if ((mask & mask_per_one)) {
1185                                                 /* bits/hex */
1186                                                 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1187                                                 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1188                                                 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1189
1190                                                 ascii_on = 1;
1191                                         }
1192                                         mask >>= bits_per_one;
1193                                 }
1194
1195                                 /* at least one bit of ascii was one -> turn ascii on */
1196                                 if (ascii_on) {
1197                                         /* ascii */
1198                                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1199                                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1200                                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1201                                 }
1202
1203                                 if (!mask)
1204                                         goto end;
1205
1206                                 line_pos++;
1207                         }
1208
1209                         start_line_pos = 0;
1210                         start_line++;
1211                 }
1212         } else { /* mask starting from end (big-endian) */
1213                 while (start_line <= stop_line) {
1214                         int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
1215                         int line_pos = stop_line_pos-1;
1216
1217                         while (line_pos >= line_pos_start) {
1218                                 int lop = 8 / bits_per_one;
1219                                 int mask_per_one = (1 << bits_per_one) - 1;
1220                                 int ascii_on = 0;
1221
1222                                 while (lop--) {
1223                                         if ((mask & mask_per_one)) {
1224                                                 /* bits/hex */
1225                                                 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
1226                                                 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
1227                                                 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1228
1229                                                 ascii_on = 1;
1230                                         }
1231                                         mask >>= bits_per_one;
1232                                 }
1233
1234                                 /* at least one bit of ascii was one -> turn ascii on */
1235                                 if (ascii_on) {
1236                                         /* ascii */
1237                                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
1238                                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
1239                                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1240                                 }
1241
1242                                 if (!mask)
1243                                         goto end;
1244
1245                                 line_pos--;
1246                         }
1247
1248                         stop_line_pos = per_line;
1249                         stop_line--;
1250                 }
1251         }
1252 end:
1253         return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
1254 #undef hex_fix
1255 #undef ascii_fix
1256 }
1257
1258 /* Update the progress bar this many times when reading a file. */
1259 #define N_PROGBAR_UPDATES       100
1260 /* The minimum packet length required to check if a progres bar is needed or not */
1261 #define MIN_PACKET_LENGTH       1024
1262
1263 /*
1264  * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1265  * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1266  * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3.  (That's
1267  * presumably why there's a progress bar for it.)
1268  *
1269  * Perhaps what's needed is a custom widget (either one that lets you stuff
1270  * text into it more quickly, or one that's a "virtual" widget so that the
1271  * text for a row is constructed, via a callback, when the row is to be
1272  * displayed).  A custom widget might also let us treat the offset, hex
1273  * data, and ASCII data as three columns, so you can select purely in
1274  * the hex dump column.
1275  */
1276 static void
1277 packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
1278 {
1279   int            i = 0, j, k = 0, b, cur;
1280   guchar         line[MAX_LINES_LEN + 1];
1281   static guchar  hexchars[16] = {
1282       '0', '1', '2', '3', '4', '5', '6', '7',
1283       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1284   static const guint8 bitmask[8] = {
1285       0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1286   guchar         c = '\0';
1287   unsigned int   use_digits;
1288   GtkTextIter    iter;
1289
1290   progdlg_t  *progbar = NULL;
1291   float       progbar_val;
1292   gboolean    progbar_stop_flag;
1293   GTimeVal    progbar_start_time;
1294   gchar       progbar_status_str[100];
1295   int         progbar_nextstep;
1296   int         progbar_quantum;
1297
1298   gtk_text_buffer_set_text(buf, "", 0);
1299   gtk_text_buffer_get_start_iter(buf, &iter);
1300
1301   /*
1302    * How many of the leading digits of the offset will we supply?
1303    * We always supply at least 4 digits, but if the maximum offset
1304    * won't fit in 4 digits, we use as many digits as will be needed.
1305    */
1306   if (((len - 1) & 0xF0000000) != 0)
1307     use_digits = 8;     /* need all 8 digits */
1308   else if (((len - 1) & 0x0F000000) != 0)
1309     use_digits = 7;     /* need 7 digits */
1310   else if (((len - 1) & 0x00F00000) != 0)
1311     use_digits = 6;     /* need 6 digits */
1312   else if (((len - 1) & 0x000F0000) != 0)
1313     use_digits = 5;     /* need 5 digits */
1314   else
1315     use_digits = 4;     /* we'll supply 4 digits */
1316
1317   /* Record the number of digits in this text view. */
1318   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1319
1320   /* Update the progress bar when it gets to this value. */
1321   if (len > MIN_PACKET_LENGTH){
1322           progbar_nextstep = 0;
1323   }else{
1324           /* If length =< MIN_PACKET_LENGTH
1325            * there is no need to calculate the progress
1326            */
1327           progbar_nextstep = len+1;
1328   }
1329
1330   /* When we reach the value that triggers a progress bar update,
1331      bump that value by this amount. */
1332   progbar_quantum = len/N_PROGBAR_UPDATES;
1333   /* Progress so far. */
1334   progbar_val = 0.0f;
1335
1336   progbar_stop_flag = FALSE;
1337   g_get_current_time(&progbar_start_time);
1338
1339   cur = 0;
1340   while (i < len) {
1341     /* Create the progress bar if necessary.
1342        We check on every iteration of the loop, so that it takes no
1343        longer than the standard time to create it (otherwise, for a
1344        large packet, we might take considerably longer than that standard
1345        time in order to get to the next progress bar step). */
1346     if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1347       progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1348                                             TRUE,
1349                                             &progbar_stop_flag,
1350                                             &progbar_start_time,
1351                                             progbar_val);
1352
1353     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1354        when we update it, we have to run the GTK+ main loop to get it
1355        to repaint what's pending, and doing so may involve an "ioctl()"
1356        to see if there's any pending input from an X server, and doing
1357        that for every packet can be costly, especially on a big file. */
1358     if (i >= progbar_nextstep) {
1359
1360       if (progbar != NULL) {
1361         /* let's not divide by zero. I should never be started
1362          * with count == 0, so let's assert that
1363          */
1364         g_assert(len > 0);
1365         progbar_val = (gfloat) i / len;
1366         g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1367                    "%4u of %u bytes", i, len);
1368         update_progress_dlg(progbar, progbar_val, progbar_status_str);
1369       }
1370
1371       progbar_nextstep += progbar_quantum;
1372     }
1373
1374     if (progbar_stop_flag) {
1375       /* Well, the user decided to abort the operation.  Just stop,
1376          and arrange to return TRUE to our caller, so they know it
1377          was stopped explicitly. */
1378       break;
1379     }
1380
1381     /* Print the line number */
1382     j = use_digits;
1383     do {
1384       j--;
1385       c = (i >> (j*4)) & 0xF;
1386       line[cur++] = hexchars[c];
1387     } while (j != 0);
1388     line[cur++] = ' ';
1389     line[cur++] = ' ';
1390
1391     j   = i;
1392     switch (recent.gui_bytes_view) {
1393     case BYTES_HEX:
1394       k = i + BYTES_PER_LINE;
1395       break;
1396     case BYTES_BITS:
1397       k = i + BITS_PER_LINE;
1398       break;
1399     default:
1400       g_assert_not_reached();
1401     }
1402     /* Print the hex bit */
1403     while (i < k) {
1404       if (i < len) {
1405         switch (recent.gui_bytes_view) {
1406         case BYTES_HEX:
1407           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1408           line[cur++] = hexchars[pd[i] & 0x0f];
1409           break;
1410         case BYTES_BITS:
1411           for (b = 0; b < 8; b++) {
1412             line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1413           }
1414           break;
1415         default:
1416           g_assert_not_reached();
1417         }
1418       } else {
1419         switch (recent.gui_bytes_view) {
1420         case BYTES_HEX:
1421           line[cur++] = ' '; line[cur++] = ' ';
1422           break;
1423         case BYTES_BITS:
1424           for (b = 0; b < 8; b++) {
1425             line[cur++] = ' ';
1426           }
1427           break;
1428         default:
1429           g_assert_not_reached();
1430         }
1431       }
1432       i++;
1433       /* Inter byte space if not at end of line */
1434       if (i < k) {
1435         line[cur++] = ' ';
1436         /* insert a space every BYTE_VIEW_SEP bytes */
1437         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1438           line[cur++] = ' ';
1439         }
1440       }
1441     }
1442
1443     /* Print some space at the end of the line */
1444     line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1445
1446     /* Print the ASCII bit */
1447     i = j;
1448
1449     while (i < k) {
1450       if (i < len) {
1451         if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
1452           c = pd[i];
1453         }
1454         else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
1455           c = EBCDIC_to_ASCII1(pd[i]);
1456         }
1457         else {
1458           g_assert_not_reached();
1459         }
1460         line[cur++] = isprint(c) ? c : '.';
1461       } else {
1462         line[cur++] = ' ';
1463       }
1464       i++;
1465       if (i < k) {
1466         /* insert a space every BYTE_VIEW_SEP bytes */
1467         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1468           line[cur++] = ' ';
1469         }
1470       }
1471     }
1472     line[cur++] = '\n';
1473     if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1474         gtk_text_buffer_insert(buf, &iter, line, cur);
1475         cur = 0;
1476     }
1477   }
1478
1479   /* We're done printing the packets; destroy the progress bar if
1480      it was created. */
1481   if (progbar != NULL)
1482     destroy_progress_dlg(progbar);
1483
1484   if (cur) {
1485         gtk_text_buffer_insert(buf, &iter, line, cur);
1486   }
1487 }
1488
1489 static void
1490 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1491                         int bend, guint32 bmask, int bmask_le,
1492                         int astart, int aend, int encoding)
1493 {
1494         GtkTextView   *bv_text_view = GTK_TEXT_VIEW(bv);
1495         GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1496         GtkTextMark   *mark;
1497         int ndigits;
1498
1499         GtkTextIter start, end;
1500
1501         g_object_ref(buf);
1502
1503 #if 0
1504         /* XXX: Apparently not a good idea; If a progress_bar
1505          *      is displayed below in delayed_create_progress_dlg()
1506          *      there will then be a crash internally in the gtk library.
1507          *      (It appears that gtk_text_view_set_buffer
1508          *       queues a callback to be run when this
1509          *       thread is next idle. Unfortunately the call to
1510          *       gtk_main_iteration() in delayed_create_progress_dlg()
1511          *       causes the internal callback to be run which then 
1512          *       crashes (because the textview has no buffer ?))
1513          */  
1514         gtk_text_view_set_buffer(bv_text_view, NULL); 
1515 #endif
1516
1517         /* attach a dummy buffer in place of the real buffer.
1518          * (XXX: Presumably this is done so there's no attempt
1519          *       to display the real buffer until it has been
1520          *       completely generated).
1521          */
1522         gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
1523
1524         packet_hex_print_common(buf, bv, pd, len, encoding);
1525
1526         /* mark everything with "plain" tag */
1527         gtk_text_buffer_get_start_iter(buf, &start);
1528         gtk_text_buffer_get_end_iter(buf, &end);
1529         gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
1530
1531         ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
1532
1533         /* mark reverse tags */
1534         mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
1535         packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
1536
1537         gtk_text_view_set_buffer(bv_text_view, buf);
1538
1539         /* scroll text into position */
1540         if (mark) {
1541                 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1542                 gtk_text_buffer_delete_mark(buf, mark);
1543         }
1544         g_object_unref(buf);
1545 }
1546
1547 void
1548 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1549                  field_info *finfo, guint len)
1550 {
1551   /* do the initial printing and save the information needed    */
1552   /* to redraw the display if preferences change.               */
1553
1554   int bstart = -1, bend = -1, blen = -1;
1555   guint32 bmask = 0x00; int bmask_le = 0;
1556   int astart = -1, aend = -1, alen = -1;
1557
1558   if (finfo != NULL) {
1559     bstart = finfo->start;
1560     blen = finfo->length;
1561     /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */    /* (value & mask) >> shift */
1562     bmask = finfo->hfinfo->bitmask;
1563     astart = finfo->appendix_start;
1564     alen = finfo->appendix_length;
1565
1566     if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1567       bmask_le = 1;
1568     else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1569       bmask_le = 0;
1570     else { /* unknown endianess - disable mask
1571       bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1572       */
1573       bmask = 0x00;
1574     }
1575
1576     if (bmask == 0x00) {
1577       int bito = FI_GET_BITS_OFFSET(finfo);
1578       int bitc = FI_GET_BITS_SIZE(finfo);
1579       int bitt = bito + bitc;
1580
1581       /* construct mask using bito & bitc */
1582       /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
1583       if (bitt > 0 && bitt < 32) {
1584         
1585         bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
1586         bmask_le = 0; /* ? */
1587      }
1588     }
1589   }
1590
1591   if (bstart >= 0 && blen > 0) {
1592     bend = bstart + blen;
1593   }
1594   if (astart >= 0 && alen > 0) {
1595     aend = astart + alen;
1596   }
1597
1598   if (bend == -1 && aend != -1) {
1599     bstart = astart;
1600     bmask = 0x00;
1601     bend = aend;
1602     astart = aend = -1;
1603   }
1604
1605   /* save the information needed to redraw the text */
1606   /* should we save the fd & finfo pointers instead ?? */
1607   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1608   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1609   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1610   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1611   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1612   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1613   g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1614                   GUINT_TO_POINTER((guint)fd->flags.encoding));
1615
1616   packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1617 }
1618
1619 /*
1620  * Redraw the text using the saved information; usually called if
1621  * the preferences have changed.
1622  */
1623 void
1624 packet_hex_reprint(GtkWidget *bv)
1625 {
1626   int start, end, mask, mask_le, encoding;
1627   int astart, aend;
1628   const guint8 *data;
1629   guint len = 0;
1630
1631   start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1632   end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1633   mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1634   mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1635   astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1636   aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1637   data = get_byte_view_data_and_length(bv, &len);
1638   g_assert(data != NULL);
1639   encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1640
1641   packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1642 }
1643
1644 /* List of all protocol tree widgets, so we can globally set the selection
1645    mode and font of all of them. */
1646 static GList *ptree_widgets;
1647
1648 /* Add a protocol tree widget to the list of protocol tree widgets. */
1649 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1650
1651 static void
1652 remember_ptree_widget(GtkWidget *ptreew)
1653 {
1654   ptree_widgets = g_list_append(ptree_widgets, ptreew);
1655
1656   /* Catch the "destroy" event on the widget, so that we remove it from
1657      the list when it's destroyed. */
1658   g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1659 }
1660
1661 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1662 static void
1663 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1664 {
1665   ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1666 }
1667
1668 /* Set the selection mode of a given packet tree window. */
1669 static void
1670 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1671 {
1672     GtkTreeSelection *selection;
1673
1674     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1675     /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1676        I think "browse" in Wireshark makes more sense than "SINGLE" in
1677        GTK+ */
1678     if (val) {
1679         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1680     }
1681     else {
1682         gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1683     }
1684 }
1685
1686 static void
1687 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1688 {
1689         set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1690 }
1691
1692 /* Set the selection mode of all packet tree windows. */
1693 void
1694 set_ptree_sel_browse_all(gboolean val)
1695 {
1696         g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1697 }
1698
1699 static void
1700 set_ptree_font_cb(gpointer data, gpointer user_data)
1701 {
1702         gtk_widget_modify_font((GtkWidget *)data,
1703                                (PangoFontDescription *)user_data);
1704 }
1705
1706 void
1707 set_ptree_font_all(PangoFontDescription *font)
1708 {
1709     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1710 }
1711
1712
1713 /*
1714  * Each expert_color_* level below should match the light gradient
1715  * colors in image/expert_indicators.svg.
1716  */
1717 gboolean colors_ok = FALSE;
1718 GdkColor        expert_color_chat       = { 0, 0x8080, 0xb7b7, 0xf7f7 };        /* light blue */
1719 GdkColor        expert_color_note       = { 0, 0xa0a0, 0xffff, 0xffff };        /* bright turquoise */
1720 GdkColor        expert_color_warn       = { 0, 0xf7f7, 0xf2f2, 0x5353 };        /* yellow */
1721 GdkColor        expert_color_error      = { 0, 0xffff, 0x5c5c, 0x5c5c };        /* pale red */
1722 GdkColor        expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 };        /* black */
1723 GdkColor        hidden_proto_item       = { 0, 0x4444, 0x4444, 0x4444 };        /* gray */
1724
1725 gchar *expert_color_chat_str;
1726 gchar *expert_color_note_str;
1727 gchar *expert_color_warn_str;
1728 gchar *expert_color_error_str;
1729 gchar *expert_color_foreground_str;
1730
1731 void proto_draw_colors_init(void)
1732 {
1733         if(colors_ok) {
1734                 return;
1735         }
1736
1737         get_color(&expert_color_chat);
1738         get_color(&expert_color_note);
1739         get_color(&expert_color_warn);
1740         get_color(&expert_color_error);
1741         get_color(&expert_color_foreground);
1742         expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1743         expert_color_note_str = gdk_color_to_string(&expert_color_note);
1744         expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1745         expert_color_error_str = gdk_color_to_string(&expert_color_error);
1746         expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1747
1748         get_color(&hidden_proto_item);
1749
1750         colors_ok = TRUE;
1751 }
1752
1753
1754 static void
1755 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1756                    GtkTreeModel *tree_model, GtkTreeIter *iter,
1757                    gpointer data _U_)
1758 {
1759     field_info   *fi;
1760
1761     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1762
1763     if(!colors_ok) {
1764         proto_draw_colors_init();
1765     }
1766
1767     /* for the various possible attributes, see:
1768      * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1769      *
1770      * color definitions can be found at:
1771      * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1772      * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1773      *
1774      * some experiences:
1775      * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1776      * weight/style: doesn't take any effect
1777      */
1778
1779     /* for each field, we have to reset the renderer attributes */
1780     g_object_set (cell, "foreground-set", FALSE, NULL);
1781
1782     g_object_set (cell, "background-set", FALSE, NULL);
1783
1784     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1785     g_object_set (cell, "underline-set", FALSE, NULL);
1786
1787     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1788     g_object_set (cell, "style-set", FALSE, NULL);*/
1789
1790     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1791     g_object_set (cell, "weight-set", FALSE, NULL);*/
1792
1793     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1794         /* we use "[...]" to mark generated items, no need to change things here */
1795
1796         /* as some fonts don't support italic, don't use this */
1797         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1798         g_object_set (cell, "style-set", TRUE, NULL);
1799         */
1800         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1801         g_object_set (cell, "weight-set", TRUE, NULL);*/
1802     }
1803
1804     if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1805         g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1806         g_object_set (cell, "foreground-set", TRUE, NULL);
1807     }
1808
1809     if (fi && fi->hfinfo) {
1810         if(fi->hfinfo->type == FT_PROTOCOL) {
1811             g_object_set (cell, "background", "gray90", NULL);
1812             g_object_set (cell, "background-set", TRUE, NULL);
1813             g_object_set (cell, "foreground", "black", NULL);
1814             g_object_set (cell, "foreground-set", TRUE, NULL);
1815             /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1816             g_object_set (cell, "weight-set", TRUE, NULL);*/
1817         }
1818         
1819         if((fi->hfinfo->type == FT_FRAMENUM) ||
1820            (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1821             render_as_url(cell);
1822         }
1823     }
1824     
1825     if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1826         switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1827         case(PI_CHAT):
1828             g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1829             g_object_set (cell, "background-set", TRUE, NULL);
1830             break;
1831         case(PI_NOTE):
1832             g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1833             g_object_set (cell, "background-set", TRUE, NULL);
1834             break;
1835         case(PI_WARN):
1836             g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1837             g_object_set (cell, "background-set", TRUE, NULL);
1838             break;
1839         case(PI_ERROR):
1840             g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1841             g_object_set (cell, "background-set", TRUE, NULL);
1842             break;
1843         default:
1844             g_assert_not_reached();
1845         }
1846         g_object_set (cell, "foreground", "black", NULL);
1847         g_object_set (cell, "foreground-set", TRUE, NULL);
1848     }
1849 }
1850
1851 GtkWidget *
1852 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1853 {
1854   GtkWidget *tv_scrollw, *tree_view;
1855   GtkTreeStore *store;
1856   GtkCellRenderer *renderer;
1857   GtkTreeViewColumn *column;
1858   gint col_offset;
1859
1860   /* Tree view */
1861   tv_scrollw = scrolled_window_new(NULL, NULL);
1862   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1863                                    GTK_SHADOW_IN);
1864
1865   store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1866   tree_view = tree_view_new(GTK_TREE_MODEL(store));
1867   g_object_unref(G_OBJECT(store));
1868   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1869   renderer = gtk_cell_renderer_text_new();
1870   g_object_set (renderer, "ypad", 0, NULL);
1871   col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1872                                                            -1, "Name", renderer,
1873                                                            "text", 0, NULL);
1874   column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1875                                     col_offset - 1);
1876   gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1877                                           NULL, NULL);
1878
1879   gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1880                                   GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1881   g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1882   g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1883   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1884   set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
1885   gtk_widget_modify_font(tree_view, user_font_get_regular());
1886   remember_ptree_widget(tree_view);
1887
1888   *tree_view_p = tree_view;
1889
1890   return tv_scrollw;
1891 }
1892
1893 void
1894 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1895 {
1896   int i;
1897   for(i=0; i < num_tree_types; i++) {
1898     tree_is_expanded[i] = TRUE;
1899   }
1900   gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1901 }
1902
1903 void
1904 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1905 {
1906   int i;
1907   for(i=0; i < num_tree_types; i++) {
1908     tree_is_expanded[i] = FALSE;
1909   }
1910   gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1911 }
1912
1913
1914 struct proto_tree_draw_info {
1915     GtkTreeView  *tree_view;
1916     GtkTreeIter  *iter;
1917 };
1918
1919 void
1920 main_proto_tree_draw(proto_tree *protocol_tree)
1921 {
1922     proto_tree_draw(protocol_tree, tree_view_gbl);
1923 }
1924
1925
1926 static void
1927 tree_view_follow_link(field_info   *fi)
1928 {
1929     gchar *url;
1930
1931     if(fi->hfinfo->type == FT_FRAMENUM) {
1932         cf_goto_frame(&cfile, fi->value.value.uinteger);
1933     }
1934     if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1935         url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1936         browser_open_url(url);
1937         g_free(url);
1938     }
1939 }
1940
1941
1942 /* If the user selected a position in the tree view, try to find
1943  * the item in the GUI proto_tree that corresponds to that byte, and
1944  * select it. */
1945 gboolean
1946 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1947 {
1948         GtkTreeSelection    *sel;
1949         GtkTreePath         *path;
1950
1951         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1952                                           (gint) (((GdkEventButton *)event)->x),
1953                                           (gint) (((GdkEventButton *)event)->y),
1954                                           &path, NULL, NULL, NULL))
1955         {
1956             sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1957
1958             /* if that's a doubleclick, try to follow the link */
1959             if(event->type == GDK_2BUTTON_PRESS) {
1960                 GtkTreeModel *model;
1961                 GtkTreeIter iter;
1962                 field_info   *fi;
1963
1964                 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1965                     gtk_tree_model_get(model, &iter, 1, &fi, -1);
1966                     tree_view_follow_link(fi);
1967                 }
1968             }
1969             else if (((GdkEventButton *)event)->button != 1) {
1970                 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1971                 gtk_tree_selection_select_path(sel, path);
1972             }
1973         } else {
1974             return FALSE;
1975         }
1976     return TRUE;
1977 }
1978
1979 /* fill the whole protocol tree with the string values */
1980 void
1981 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1982 {
1983     GtkTreeStore *store;
1984     struct proto_tree_draw_info info;
1985
1986     info.tree_view = GTK_TREE_VIEW(tree_view);
1987     info.iter = NULL;
1988
1989     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1990
1991     /*
1992      * Clear out any crud left over in the display of the protocol
1993      * tree, by removing all nodes from the tree.
1994      * This is how it's done in testgtk.c in GTK+.
1995      */
1996     gtk_tree_store_clear(store);
1997
1998     proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1999 }
2000
2001
2002 /* fill a single protocol tree item with the string value */
2003 static void
2004 proto_tree_draw_node(proto_node *node, gpointer data)
2005 {
2006     struct proto_tree_draw_info info;
2007     struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2008
2009     field_info   *fi = PNODE_FINFO(node);
2010     gchar         label_str[ITEM_LABEL_LENGTH];
2011     gchar        *label_ptr;
2012     gboolean      is_leaf, is_expanded;
2013     GtkTreeStore *store;
2014     GtkTreeIter   iter;
2015     GtkTreePath  *path;
2016
2017     g_assert(fi && "dissection with an invisible proto tree?");
2018
2019     if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2020         return;
2021
2022     /* was a free format label produced? */
2023     if (fi->rep) {
2024         label_ptr = fi->rep->representation;
2025     }
2026     else { /* no, make a generic label */
2027         label_ptr = label_str;
2028         proto_item_fill_label(fi, label_str);
2029     }
2030
2031     if (node->first_child != NULL) {
2032         is_leaf = FALSE;
2033         g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2034         if (tree_is_expanded[fi->tree_type]) {
2035             is_expanded = TRUE;
2036         }
2037         else {
2038             is_expanded = FALSE;
2039         }
2040     }
2041     else {
2042         is_leaf = TRUE;
2043         is_expanded = FALSE;
2044     }
2045
2046     if (PROTO_ITEM_IS_GENERATED(node)) {
2047         if (PROTO_ITEM_IS_HIDDEN(node)) {
2048             label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2049         } else {
2050             label_ptr = g_strdup_printf("[%s]", label_ptr);
2051         }
2052     } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2053         label_ptr = g_strdup_printf("<%s>", label_ptr);
2054     }
2055
2056     info.tree_view = parent_info->tree_view;
2057     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2058     gtk_tree_store_append(store, &iter, parent_info->iter);
2059     gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2060
2061     if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2062         g_free(label_ptr);
2063     }
2064
2065     if (!is_leaf) {
2066         info.iter = &iter;
2067         proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2068         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2069         if (is_expanded)
2070             gtk_tree_view_expand_to_path(info.tree_view, path);
2071         else
2072             gtk_tree_view_collapse_row(info.tree_view, path);
2073         gtk_tree_path_free(path);
2074     }
2075 }
2076
2077 /*
2078  * Clear the hex dump and protocol tree panes.
2079  */
2080 void
2081 clear_tree_and_hex_views(void)
2082 {
2083   /* Clear the hex dump by getting rid of all the byte views. */
2084   while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2085     gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2086
2087   /* Add a placeholder byte view so that there's at least something
2088      displayed in the byte view notebook. */
2089   add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2090
2091   /* Clear the protocol tree by removing all nodes in the ctree.
2092      This is how it's done in testgtk.c in GTK+ */
2093   gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2094 }
2095
2096 void
2097 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2098 {
2099   if (recent.gui_bytes_view != view) {
2100     recent.gui_bytes_view = view;
2101     redraw_packet_bytes_all();
2102   }
2103 }