Fix build by #if 0 out unused de_sgsap_tmsi() function.
[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 "win32/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 gboolean
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); /* triggers byte_view_realize_cb which calls packet_hex_print */
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 */
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(GtkWidget *w _U_, gpointer user_data _U_)
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 bstart, int bend, 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 (bstart == -1 || bend == -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 = bstart / per_line;
1139     start_line_pos = bstart % per_line;
1140
1141     stop_line = bend / per_line;
1142     stop_line_pos = bend % 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             int first_block_adjust = (recent.gui_bytes_view == BYTES_HEX) ? (line_pos_end == per_line/2) : 0;
1157
1158             if (start_line_pos == line_pos_end) break;
1159
1160             /* bits/hex */
1161             gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
1162             gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1-first_block_adjust);
1163             gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1164
1165             /* ascii */
1166             gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
1167             gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end)-first_block_adjust);
1168             gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1169
1170             start_line_pos = 0;
1171             start_line++;
1172         }
1173
1174     } else if (mask_le) { /* LSB of mask first (little-endian) */
1175         while (start_line <= stop_line) {
1176             int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1177             int line_pos = start_line_pos;
1178
1179             while (line_pos < line_pos_end) {
1180                 int lop = 8 / bits_per_one;
1181                 int mask_per_one = (1 << bits_per_one) - 1;
1182                 int ascii_on = 0;
1183
1184                 while (lop--) {
1185                     if ((mask & mask_per_one)) {
1186                         /* bits/hex */
1187                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1188                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1189                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1190
1191                         ascii_on = 1;
1192                     }
1193                     mask >>= bits_per_one;
1194                 }
1195
1196                 /* at least one bit of ascii was one -> turn ascii on */
1197                 if (ascii_on) {
1198                     /* ascii */
1199                     gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1200                     gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1201                     gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1202                 }
1203
1204                 if (!mask)
1205                     goto xend;
1206
1207                 line_pos++;
1208             }
1209
1210             start_line_pos = 0;
1211             start_line++;
1212         }
1213     } else { /* mask starting from end (big-endian) */
1214         while (start_line <= stop_line) {
1215             int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
1216             int line_pos = stop_line_pos-1;
1217
1218             while (line_pos >= line_pos_start) {
1219                 int lop = 8 / bits_per_one;
1220                 int mask_per_one = (1 << bits_per_one) - 1;
1221                 int ascii_on = 0;
1222
1223                 while (lop--) {
1224                     if ((mask & mask_per_one)) {
1225                         /* bits/hex */
1226                         gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
1227                         gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
1228                         gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1229
1230                         ascii_on = 1;
1231                     }
1232                     mask >>= bits_per_one;
1233                 }
1234
1235                 /* at least one bit of ascii was one -> turn ascii on */
1236                 if (ascii_on) {
1237                     /* ascii */
1238                     gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
1239                     gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
1240                     gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1241                 }
1242
1243                 if (!mask)
1244                     goto xend;
1245
1246                 line_pos--;
1247             }
1248
1249             stop_line_pos = per_line;
1250             stop_line--;
1251         }
1252     }
1253 xend:
1254     return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
1255 #undef hex_fix
1256 #undef ascii_fix
1257 }
1258
1259 /* Update the progress bar this many times when reading a file. */
1260 #define N_PROGBAR_UPDATES        100
1261 /* The minimum packet length required to check if a progress bar is needed or not */
1262 #define MIN_PACKET_LENGTH       1024
1263
1264 /*
1265  * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1266  * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1267  * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3.  (That's
1268  * presumably why there's a progress bar for it.)
1269  *
1270  * Perhaps what's needed is a custom widget (either one that lets you stuff
1271  * text into it more quickly, or one that's a "virtual" widget so that the
1272  * text for a row is constructed, via a callback, when the row is to be
1273  * displayed).  A custom widget might also let us treat the offset, hex
1274  * data, and ASCII data as three columns, so you can select purely in
1275  * the hex dump column.
1276  */
1277 static void
1278 packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
1279 {
1280     int            i = 0, j, k = 0, b, cur;
1281     guchar         line[MAX_LINES_LEN + 1];
1282     static guchar  hexchars[16] = {
1283         '0', '1', '2', '3', '4', '5', '6', '7',
1284         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1285     static const guint8 bitmask[8] = {
1286         0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1287     guchar         c = '\0';
1288     unsigned int   use_digits;
1289     GtkTextIter    iter;
1290
1291     progdlg_t  *progbar = NULL;
1292     float       progbar_val;
1293     gboolean    progbar_stop_flag;
1294     GTimeVal    progbar_start_time;
1295     gchar       progbar_status_str[100];
1296     int         progbar_nextstep;
1297     int         progbar_quantum;
1298
1299     gtk_text_buffer_set_text(buf, "", 0);
1300     gtk_text_buffer_get_start_iter(buf, &iter);
1301
1302     /*
1303      * How many of the leading digits of the offset will we supply?
1304      * We always supply at least 4 digits, but if the maximum offset
1305      * won't fit in 4 digits, we use as many digits as will be needed.
1306      */
1307     if (((len - 1) & 0xF0000000) != 0)
1308         use_digits = 8; /* need all 8 digits */
1309     else if (((len - 1) & 0x0F000000) != 0)
1310         use_digits = 7; /* need 7 digits */
1311     else if (((len - 1) & 0x00F00000) != 0)
1312         use_digits = 6; /* need 6 digits */
1313     else if (((len - 1) & 0x000F0000) != 0)
1314         use_digits = 5; /* need 5 digits */
1315     else
1316         use_digits = 4; /* we'll supply 4 digits */
1317
1318     /* Record the number of digits in this text view. */
1319     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1320
1321     /* Update the progress bar when it gets to this value. */
1322     if (len > MIN_PACKET_LENGTH){
1323         progbar_nextstep = 0;
1324     }else{
1325         /* If length =< MIN_PACKET_LENGTH
1326          * there is no need to calculate the progress
1327          */
1328         progbar_nextstep = len+1;
1329     }
1330
1331     /* When we reach the value that triggers a progress bar update,
1332        bump that value by this amount. */
1333     progbar_quantum = len/N_PROGBAR_UPDATES;
1334     /* Progress so far. */
1335     progbar_val = 0.0f;
1336
1337     progbar_stop_flag = FALSE;
1338     g_get_current_time(&progbar_start_time);
1339
1340     cur = 0;
1341     while (i < len) {
1342         /* Create the progress bar if necessary.
1343            We check on every iteration of the loop, so that it takes no
1344            longer than the standard time to create it (otherwise, for a
1345            large packet, we might take considerably longer than that standard
1346            time in order to get to the next progress bar step). */
1347         if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1348             progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1349                                                   TRUE,
1350                                                   &progbar_stop_flag,
1351                                                   &progbar_start_time,
1352                                                   progbar_val);
1353
1354         /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1355            when we update it, we have to run the GTK+ main loop to get it
1356            to repaint what's pending, and doing so may involve an "ioctl()"
1357            to see if there's any pending input from an X server, and doing
1358            that for every packet can be costly, especially on a big file. */
1359         if (i >= progbar_nextstep) {
1360
1361             if (progbar != NULL) {
1362                 /* let's not divide by zero. I should never be started
1363                  * with count == 0, so let's assert that
1364                  */
1365                 g_assert(len > 0);
1366                 progbar_val = (gfloat) i / len;
1367                 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1368                            "%4u of %u bytes", i, len);
1369                 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1370             }
1371
1372             progbar_nextstep += progbar_quantum;
1373         }
1374
1375         if (progbar_stop_flag) {
1376             /* Well, the user decided to abort the operation.  Just stop,
1377                and arrange to return TRUE to our caller, so they know it
1378                was stopped explicitly. */
1379             break;
1380         }
1381
1382         /* Print the line number */
1383         j = use_digits;
1384         do {
1385             j--;
1386             c = (i >> (j*4)) & 0xF;
1387             line[cur++] = hexchars[c];
1388         } while (j != 0);
1389         line[cur++] = ' ';
1390         line[cur++] = ' ';
1391
1392         j   = i;
1393         switch (recent.gui_bytes_view) {
1394         case BYTES_HEX:
1395             k = i + BYTES_PER_LINE;
1396             break;
1397         case BYTES_BITS:
1398             k = i + BITS_PER_LINE;
1399             break;
1400         default:
1401             g_assert_not_reached();
1402         }
1403         /* Print the hex bit */
1404         while (i < k) {
1405             if (i < len) {
1406                 switch (recent.gui_bytes_view) {
1407                 case BYTES_HEX:
1408                     line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1409                     line[cur++] = hexchars[pd[i] & 0x0f];
1410                     break;
1411                 case BYTES_BITS:
1412                     for (b = 0; b < 8; b++) {
1413                         line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1414                     }
1415                     break;
1416                 default:
1417                     g_assert_not_reached();
1418                 }
1419             } else {
1420                 switch (recent.gui_bytes_view) {
1421                 case BYTES_HEX:
1422                     line[cur++] = ' '; line[cur++] = ' ';
1423                     break;
1424                 case BYTES_BITS:
1425                     for (b = 0; b < 8; b++) {
1426                         line[cur++] = ' ';
1427                     }
1428                     break;
1429                 default:
1430                     g_assert_not_reached();
1431                 }
1432             }
1433             i++;
1434             /* Inter byte space if not at end of line */
1435             if (i < k) {
1436                 line[cur++] = ' ';
1437                 /* insert a space every BYTE_VIEW_SEP bytes */
1438                 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1439                     line[cur++] = ' ';
1440                 }
1441             }
1442         }
1443
1444         /* Print some space at the end of the line */
1445         line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1446
1447         /* Print the ASCII bit */
1448         i = j;
1449
1450         while (i < k) {
1451             if (i < len) {
1452                 if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
1453                     c = pd[i];
1454                 }
1455                 else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
1456                     c = EBCDIC_to_ASCII1(pd[i]);
1457                 }
1458                 else {
1459                     g_assert_not_reached();
1460                 }
1461                 line[cur++] = isprint(c) ? c : '.';
1462             } else {
1463                 line[cur++] = ' ';
1464             }
1465             i++;
1466             if (i < k) {
1467                 /* insert a space every BYTE_VIEW_SEP bytes */
1468                 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1469                     line[cur++] = ' ';
1470                 }
1471             }
1472         }
1473         line[cur++] = '\n';
1474         if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1475             gtk_text_buffer_insert(buf, &iter, line, cur);
1476             cur = 0;
1477         }
1478     }
1479
1480     /* We're done printing the packets; destroy the progress bar if
1481        it was created. */
1482     if (progbar != NULL)
1483         destroy_progress_dlg(progbar);
1484
1485     if (cur) {
1486         gtk_text_buffer_insert(buf, &iter, line, cur);
1487     }
1488 }
1489
1490 static void
1491 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1492                   int bend, guint32 bmask, int bmask_le,
1493                   int astart, int aend, int encoding)
1494 {
1495     GtkTextView   *bv_text_view = GTK_TEXT_VIEW(bv);
1496     GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1497     GtkTextMark   *mark;
1498     int ndigits;
1499
1500     GtkTextIter start, end;
1501
1502     g_object_ref(buf);
1503
1504 #if 0
1505     /* XXX: Setting the text_view buffer to NULL is apparently
1506      *      not a good idea; If a progress_bar is displayed below
1507      *      in delayed_create_progress_dlg() there will then be
1508      *      a crash internally in the gtk library.
1509      *      (It appears that gtk_text_view_set_buffer
1510      *       queues a callback to be run when this
1511      *       thread is next idle. Unfortunately the call to
1512      *       gtk_main_iteration() in delayed_create_progress_dlg()
1513      *       causes the internal callback to be run which then
1514      *       crashes (because the textview has no buffer ?))
1515      */
1516     gtk_text_view_set_buffer(bv_text_view, NULL);
1517 #endif
1518
1519     /* attach a dummy buffer in place of the real buffer.
1520      * (XXX: Presumably this is done so there's no attempt
1521      *       to display the real buffer until it has been
1522      *       completely generated).
1523      */
1524     gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
1525
1526     packet_hex_print_common(buf, bv, pd, len, encoding);
1527
1528     /* mark everything with "plain" tag */
1529     gtk_text_buffer_get_start_iter(buf, &start);
1530     gtk_text_buffer_get_end_iter(buf, &end);
1531     gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
1532
1533     ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
1534
1535     /* mark reverse tags */
1536     mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
1537     packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
1538
1539     gtk_text_view_set_buffer(bv_text_view, buf);
1540
1541     /* scroll text into position */
1542     if (mark) {
1543         gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1544         gtk_text_buffer_delete_mark(buf, mark);
1545     }
1546     g_object_unref(buf);
1547 }
1548
1549 void
1550 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1551                  field_info *finfo, guint len)
1552 {
1553     /* do the initial printing and save the information needed  */
1554     /* to redraw the display if preferences change.             */
1555
1556     int bstart = -1, bend = -1, blen = -1;
1557     guint32 bmask = 0x00; int bmask_le = 0;
1558     int astart = -1, aend = -1, alen = -1;
1559
1560
1561     if (finfo != NULL) {
1562         bstart = finfo->start;
1563         blen = finfo->length;
1564         /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
1565         bmask = finfo->hfinfo->bitmask;
1566         astart = finfo->appendix_start;
1567         alen = finfo->appendix_length;
1568
1569         if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1570             bmask_le = 1;
1571         else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1572             bmask_le = 0;
1573         else { /* unknown endianess - disable mask
1574                   bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1575                */
1576             bmask = 0x00;
1577         }
1578
1579         if (bmask == 0x00) {
1580             int bito = FI_GET_BITS_OFFSET(finfo);
1581             int bitc = FI_GET_BITS_SIZE(finfo);
1582             int bitt = bito + bitc;
1583
1584             /* construct mask using bito & bitc */
1585             /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
1586             if (bitt > 0 && bitt < 32) {
1587
1588                 bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
1589                 bmask_le = 0; /* ? */
1590             }
1591         }
1592     }
1593
1594     if (bstart >= 0 && blen > 0) {
1595         bend = bstart + blen;
1596     }
1597     if (astart >= 0 && alen > 0) {
1598         aend = astart + alen;
1599     }
1600
1601     if (bend == -1 && aend != -1) {
1602         bstart = astart;
1603         bmask = 0x00;
1604         bend = aend;
1605         astart = aend = -1;
1606     }
1607
1608     /* don't exceed the end of available data */
1609     if (aend != -1 && (guint)aend > len) aend = len;
1610     if (bend != -1 && (guint)bend > len) bend = len;
1611
1612     /* save the information needed to redraw the text */
1613     /* should we save the fd & finfo pointers instead ?? */
1614     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1615     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1616     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1617     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1618     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1619     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1620     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1621                       GUINT_TO_POINTER((guint)fd->flags.encoding));
1622
1623     packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1624 }
1625
1626 /*
1627  * Redraw the text using the saved information; usually called if
1628  * the preferences have changed.
1629  */
1630 void
1631 packet_hex_reprint(GtkWidget *bv)
1632 {
1633     int start, end, mask, mask_le, encoding;
1634     int astart, aend;
1635     const guint8 *data;
1636     guint len = 0;
1637
1638     start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1639     end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1640     mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1641     mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1642     astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1643     aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1644     data = get_byte_view_data_and_length(bv, &len);
1645     g_assert(data != NULL);
1646     encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1647
1648     packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1649 }
1650
1651 /* List of all protocol tree widgets, so we can globally set the selection
1652    mode and font of all of them. */
1653 static GList *ptree_widgets;
1654
1655 /* Add a protocol tree widget to the list of protocol tree widgets. */
1656 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1657
1658 static void
1659 remember_ptree_widget(GtkWidget *ptreew)
1660 {
1661     ptree_widgets = g_list_append(ptree_widgets, ptreew);
1662
1663     /* Catch the "destroy" event on the widget, so that we remove it from
1664        the list when it's destroyed. */
1665     g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1666 }
1667
1668 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1669 static void
1670 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1671 {
1672     ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1673 }
1674
1675 /* Set the selection mode of a given packet tree window. */
1676 static void
1677 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1678 {
1679     GtkTreeSelection *selection;
1680
1681     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1682     /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1683        I think "browse" in Wireshark makes more sense than "SINGLE" in
1684        GTK+ */
1685     if (val) {
1686         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1687     }
1688     else {
1689         gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1690     }
1691 }
1692
1693 static void
1694 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1695 {
1696     set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1697 }
1698
1699 /* Set the selection mode of all packet tree windows. */
1700 void
1701 set_ptree_sel_browse_all(gboolean val)
1702 {
1703     g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1704 }
1705
1706 static void
1707 set_ptree_font_cb(gpointer data, gpointer user_data)
1708 {
1709     gtk_widget_modify_font((GtkWidget *)data,
1710                            (PangoFontDescription *)user_data);
1711 }
1712
1713 void
1714 set_ptree_font_all(PangoFontDescription *font)
1715 {
1716     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1717 }
1718
1719
1720 /*
1721  * Each expert_color_* level below should match the light gradient
1722  * colors in image/expert_indicators.svg.
1723  */
1724 static gboolean colors_ok = FALSE;
1725
1726 GdkColor        expert_color_chat       = { 0, 0x8080, 0xb7b7, 0xf7f7 };        /* light blue */
1727 GdkColor        expert_color_note       = { 0, 0xa0a0, 0xffff, 0xffff };        /* bright turquoise */
1728 GdkColor        expert_color_warn       = { 0, 0xf7f7, 0xf2f2, 0x5353 };        /* yellow */
1729 GdkColor        expert_color_error      = { 0, 0xffff, 0x5c5c, 0x5c5c };        /* pale red */
1730 GdkColor        expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 };        /* black */
1731 GdkColor        hidden_proto_item       = { 0, 0x4444, 0x4444, 0x4444 };        /* gray */
1732
1733 gchar *expert_color_chat_str;
1734 gchar *expert_color_note_str;
1735 gchar *expert_color_warn_str;
1736 gchar *expert_color_error_str;
1737 gchar *expert_color_foreground_str;
1738
1739 void proto_draw_colors_init(void)
1740 {
1741     if(colors_ok) {
1742         return;
1743     }
1744
1745     get_color(&expert_color_chat);
1746     get_color(&expert_color_note);
1747     get_color(&expert_color_warn);
1748     get_color(&expert_color_error);
1749     get_color(&expert_color_foreground);
1750     expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1751     expert_color_note_str = gdk_color_to_string(&expert_color_note);
1752     expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1753     expert_color_error_str = gdk_color_to_string(&expert_color_error);
1754     expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1755
1756     get_color(&hidden_proto_item);
1757
1758     colors_ok = TRUE;
1759 }
1760
1761
1762 static void
1763 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1764                    GtkTreeModel *tree_model, GtkTreeIter *iter,
1765                    gpointer data _U_)
1766 {
1767     field_info   *fi;
1768
1769     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1770
1771     if(!colors_ok) {
1772         proto_draw_colors_init();
1773     }
1774
1775     /* for the various possible attributes, see:
1776      * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1777      *
1778      * color definitions can be found at:
1779      * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1780      * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1781      *
1782      * some experiences:
1783      * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1784      * weight/style: doesn't take any effect
1785      */
1786
1787     /* for each field, we have to reset the renderer attributes */
1788     g_object_set (cell, "foreground-set", FALSE, NULL);
1789
1790     g_object_set (cell, "background-set", FALSE, NULL);
1791
1792     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1793     g_object_set (cell, "underline-set", FALSE, NULL);
1794
1795     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1796     g_object_set (cell, "style-set", FALSE, NULL);*/
1797
1798     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1799     g_object_set (cell, "weight-set", FALSE, NULL);*/
1800
1801     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1802         /* we use "[...]" to mark generated items, no need to change things here */
1803
1804         /* as some fonts don't support italic, don't use this */
1805         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1806         g_object_set (cell, "style-set", TRUE, NULL);
1807         */
1808         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1809         g_object_set (cell, "weight-set", TRUE, NULL);*/
1810     }
1811
1812     if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1813         g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1814         g_object_set (cell, "foreground-set", TRUE, NULL);
1815     }
1816
1817     if (fi && fi->hfinfo) {
1818         if(fi->hfinfo->type == FT_PROTOCOL) {
1819             g_object_set (cell, "background", "gray90", NULL);
1820             g_object_set (cell, "background-set", TRUE, NULL);
1821             g_object_set (cell, "foreground", "black", NULL);
1822             g_object_set (cell, "foreground-set", TRUE, NULL);
1823             /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1824             g_object_set (cell, "weight-set", TRUE, NULL);*/
1825         }
1826
1827         if((fi->hfinfo->type == FT_FRAMENUM) ||
1828            (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1829             render_as_url(cell);
1830         }
1831     }
1832
1833     if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1834         switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1835         case(PI_CHAT):
1836             g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1837             g_object_set (cell, "background-set", TRUE, NULL);
1838             break;
1839         case(PI_NOTE):
1840             g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1841             g_object_set (cell, "background-set", TRUE, NULL);
1842             break;
1843         case(PI_WARN):
1844             g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1845             g_object_set (cell, "background-set", TRUE, NULL);
1846             break;
1847         case(PI_ERROR):
1848             g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1849             g_object_set (cell, "background-set", TRUE, NULL);
1850             break;
1851         default:
1852             g_assert_not_reached();
1853         }
1854         g_object_set (cell, "foreground", "black", NULL);
1855         g_object_set (cell, "foreground-set", TRUE, NULL);
1856     }
1857 }
1858
1859 GtkWidget *
1860 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1861 {
1862     GtkWidget *tv_scrollw, *tree_view;
1863     GtkTreeStore *store;
1864     GtkCellRenderer *renderer;
1865     GtkTreeViewColumn *column;
1866     gint col_offset;
1867
1868     /* Tree view */
1869     tv_scrollw = scrolled_window_new(NULL, NULL);
1870     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1871                                         GTK_SHADOW_IN);
1872
1873     store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1874     tree_view = tree_view_new(GTK_TREE_MODEL(store));
1875     g_object_unref(G_OBJECT(store));
1876     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1877     renderer = gtk_cell_renderer_text_new();
1878     g_object_set (renderer, "ypad", 0, NULL);
1879     col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1880                                                              -1, "Name", renderer,
1881                                                              "text", 0, NULL);
1882     column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1883                                       col_offset - 1);
1884     gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1885                                             NULL, NULL);
1886
1887     gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1888                                     GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1889     g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1890     g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1891     gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1892     set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
1893     gtk_widget_modify_font(tree_view, user_font_get_regular());
1894     remember_ptree_widget(tree_view);
1895
1896     *tree_view_p = tree_view;
1897
1898     return tv_scrollw;
1899 }
1900
1901 void
1902 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1903 {
1904     int i;
1905     for(i=0; i < num_tree_types; i++) {
1906         tree_is_expanded[i] = TRUE;
1907     }
1908     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1909 }
1910
1911 void
1912 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1913 {
1914     int i;
1915     for(i=0; i < num_tree_types; i++) {
1916         tree_is_expanded[i] = FALSE;
1917     }
1918     gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1919 }
1920
1921
1922 struct proto_tree_draw_info {
1923     GtkTreeView  *tree_view;
1924     GtkTreeIter  *iter;
1925 };
1926
1927 void
1928 main_proto_tree_draw(proto_tree *protocol_tree)
1929 {
1930     proto_tree_draw(protocol_tree, tree_view_gbl);
1931 }
1932
1933
1934 static void
1935 tree_view_follow_link(field_info   *fi)
1936 {
1937     gchar *url;
1938
1939     if(fi->hfinfo->type == FT_FRAMENUM) {
1940         cf_goto_frame(&cfile, fi->value.value.uinteger);
1941     }
1942     if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1943         url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1944         browser_open_url(url);
1945         g_free(url);
1946     }
1947 }
1948
1949
1950 /* If the user selected a position in the tree view, try to find
1951  * the item in the GUI proto_tree that corresponds to that byte, and
1952  * select it. */
1953 gboolean
1954 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1955 {
1956     GtkTreeSelection    *sel;
1957     GtkTreePath         *path;
1958
1959     if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1960                                       (gint) (((GdkEventButton *)event)->x),
1961                                       (gint) (((GdkEventButton *)event)->y),
1962                                       &path, NULL, NULL, NULL))
1963     {
1964         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1965
1966         /* if that's a doubleclick, try to follow the link */
1967         if(event->type == GDK_2BUTTON_PRESS) {
1968             GtkTreeModel *model;
1969             GtkTreeIter iter;
1970             field_info   *fi;
1971
1972             if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1973                 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1974                 tree_view_follow_link(fi);
1975             }
1976         }
1977         else if (((GdkEventButton *)event)->button != 1) {
1978             /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1979             gtk_tree_selection_select_path(sel, path);
1980         }
1981     } else {
1982         return FALSE;
1983     }
1984     return TRUE;
1985 }
1986
1987 /* fill the whole protocol tree with the string values */
1988 void
1989 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1990 {
1991     GtkTreeStore *store;
1992     struct proto_tree_draw_info info;
1993
1994     info.tree_view = GTK_TREE_VIEW(tree_view);
1995     info.iter = NULL;
1996
1997     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1998
1999     /*
2000      * Clear out any crud left over in the display of the protocol
2001      * tree, by removing all nodes from the tree.
2002      * This is how it's done in testgtk.c in GTK+.
2003      */
2004     gtk_tree_store_clear(store);
2005
2006     proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2007 }
2008
2009
2010 /* fill a single protocol tree item with the string value */
2011 static void
2012 proto_tree_draw_node(proto_node *node, gpointer data)
2013 {
2014     struct proto_tree_draw_info  info;
2015     struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2016
2017     field_info   *fi = PNODE_FINFO(node);
2018     gchar         label_str[ITEM_LABEL_LENGTH];
2019     gchar        *label_ptr;
2020     gboolean      is_leaf, is_expanded;
2021     GtkTreeStore *store;
2022     GtkTreeIter   iter;
2023     GtkTreePath  *path;
2024
2025     g_assert(fi && "dissection with an invisible proto tree?");
2026
2027     if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2028         return;
2029
2030     /* was a free format label produced? */
2031     if (fi->rep) {
2032         label_ptr = fi->rep->representation;
2033     }
2034     else { /* no, make a generic label */
2035         label_ptr = label_str;
2036         proto_item_fill_label(fi, label_str);
2037     }
2038
2039     if (node->first_child != NULL) {
2040         is_leaf = FALSE;
2041         g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2042         if (tree_is_expanded[fi->tree_type]) {
2043             is_expanded = TRUE;
2044         }
2045         else {
2046             is_expanded = FALSE;
2047         }
2048     }
2049     else {
2050         is_leaf = TRUE;
2051         is_expanded = FALSE;
2052     }
2053
2054     if (PROTO_ITEM_IS_GENERATED(node)) {
2055         if (PROTO_ITEM_IS_HIDDEN(node)) {
2056             label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2057         } else {
2058             label_ptr = g_strdup_printf("[%s]", label_ptr);
2059         }
2060     } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2061         label_ptr = g_strdup_printf("<%s>", label_ptr);
2062     }
2063
2064     info.tree_view = parent_info->tree_view;
2065     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2066     gtk_tree_store_append(store, &iter, parent_info->iter);
2067     gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2068
2069     if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2070         g_free(label_ptr);
2071     }
2072
2073     if (!is_leaf) {
2074         info.iter = &iter;
2075         proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2076         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2077         if (is_expanded)
2078             gtk_tree_view_expand_to_path(info.tree_view, path);
2079         else
2080             gtk_tree_view_collapse_row(info.tree_view, path);
2081         gtk_tree_path_free(path);
2082     }
2083 }
2084
2085 /*
2086  * Clear the hex dump and protocol tree panes.
2087  */
2088 void
2089 clear_tree_and_hex_views(void)
2090 {
2091     /* Clear the hex dump by getting rid of all the byte views. */
2092     while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2093         gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2094
2095     /* Add a placeholder byte view so that there's at least something
2096        displayed in the byte view notebook. */
2097     add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2098
2099     /* Clear the protocol tree by removing all nodes in the ctree.
2100        This is how it's done in testgtk.c in GTK+ */
2101     gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2102 }
2103
2104 void
2105 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2106 {
2107     if (recent.gui_bytes_view != view) {
2108         recent.gui_bytes_view = view;
2109         redraw_packet_bytes_all();
2110     }
2111 }