From Alexander Koeppe:
[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 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     if (finfo != NULL) {
1561         bstart = finfo->start;
1562         blen = finfo->length;
1563         if ( blen > tvb_reported_length_remaining(finfo->ds_tvb, bstart) )
1564                 blen = tvb_reported_length_remaining(finfo->ds_tvb, bstart);
1565         /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
1566         bmask = finfo->hfinfo->bitmask;
1567         astart = finfo->appendix_start;
1568         alen = finfo->appendix_length;
1569
1570         if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1571             bmask_le = 1;
1572         else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1573             bmask_le = 0;
1574         else { /* unknown endianess - disable mask
1575                   bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1576                */
1577             bmask = 0x00;
1578         }
1579
1580         if (bmask == 0x00) {
1581             int bito = FI_GET_BITS_OFFSET(finfo);
1582             int bitc = FI_GET_BITS_SIZE(finfo);
1583             int bitt = bito + bitc;
1584
1585             /* construct mask using bito & bitc */
1586             /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
1587             if (bitt > 0 && bitt < 32) {
1588
1589                 bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
1590                 bmask_le = 0; /* ? */
1591             }
1592         }
1593     }
1594
1595     if (bstart >= 0 && blen > 0) {
1596         bend = bstart + blen;
1597     }
1598     if (astart >= 0 && alen > 0) {
1599         aend = astart + alen;
1600     }
1601
1602     if (bend == -1 && aend != -1) {
1603         bstart = astart;
1604         bmask = 0x00;
1605         bend = aend;
1606         astart = aend = -1;
1607     }
1608
1609     /* save the information needed to redraw the text */
1610     /* should we save the fd & finfo pointers instead ?? */
1611     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1612     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1613     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1614     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1615     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1616     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1617     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1618                       GUINT_TO_POINTER((guint)fd->flags.encoding));
1619
1620     packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1621 }
1622
1623 /*
1624  * Redraw the text using the saved information; usually called if
1625  * the preferences have changed.
1626  */
1627 void
1628 packet_hex_reprint(GtkWidget *bv)
1629 {
1630     int start, end, mask, mask_le, encoding;
1631     int astart, aend;
1632     const guint8 *data;
1633     guint len = 0;
1634
1635     start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1636     end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1637     mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1638     mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1639     astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1640     aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1641     data = get_byte_view_data_and_length(bv, &len);
1642     g_assert(data != NULL);
1643     encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1644
1645     packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1646 }
1647
1648 /* List of all protocol tree widgets, so we can globally set the selection
1649    mode and font of all of them. */
1650 static GList *ptree_widgets;
1651
1652 /* Add a protocol tree widget to the list of protocol tree widgets. */
1653 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1654
1655 static void
1656 remember_ptree_widget(GtkWidget *ptreew)
1657 {
1658     ptree_widgets = g_list_append(ptree_widgets, ptreew);
1659
1660     /* Catch the "destroy" event on the widget, so that we remove it from
1661        the list when it's destroyed. */
1662     g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1663 }
1664
1665 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1666 static void
1667 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1668 {
1669     ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1670 }
1671
1672 /* Set the selection mode of a given packet tree window. */
1673 static void
1674 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1675 {
1676     GtkTreeSelection *selection;
1677
1678     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1679     /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1680        I think "browse" in Wireshark makes more sense than "SINGLE" in
1681        GTK+ */
1682     if (val) {
1683         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1684     }
1685     else {
1686         gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1687     }
1688 }
1689
1690 static void
1691 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1692 {
1693     set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1694 }
1695
1696 /* Set the selection mode of all packet tree windows. */
1697 void
1698 set_ptree_sel_browse_all(gboolean val)
1699 {
1700     g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1701 }
1702
1703 static void
1704 set_ptree_font_cb(gpointer data, gpointer user_data)
1705 {
1706     gtk_widget_modify_font((GtkWidget *)data,
1707                            (PangoFontDescription *)user_data);
1708 }
1709
1710 void
1711 set_ptree_font_all(PangoFontDescription *font)
1712 {
1713     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1714 }
1715
1716
1717 /*
1718  * Each expert_color_* level below should match the light gradient
1719  * colors in image/expert_indicators.svg.
1720  */
1721 static gboolean colors_ok = FALSE;
1722
1723 GdkColor        expert_color_chat       = { 0, 0x8080, 0xb7b7, 0xf7f7 };        /* light blue */
1724 GdkColor        expert_color_note       = { 0, 0xa0a0, 0xffff, 0xffff };        /* bright turquoise */
1725 GdkColor        expert_color_warn       = { 0, 0xf7f7, 0xf2f2, 0x5353 };        /* yellow */
1726 GdkColor        expert_color_error      = { 0, 0xffff, 0x5c5c, 0x5c5c };        /* pale red */
1727 GdkColor        expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 };        /* black */
1728 GdkColor        hidden_proto_item       = { 0, 0x4444, 0x4444, 0x4444 };        /* gray */
1729
1730 gchar *expert_color_chat_str;
1731 gchar *expert_color_note_str;
1732 gchar *expert_color_warn_str;
1733 gchar *expert_color_error_str;
1734 gchar *expert_color_foreground_str;
1735
1736 void proto_draw_colors_init(void)
1737 {
1738     if(colors_ok) {
1739         return;
1740     }
1741
1742     get_color(&expert_color_chat);
1743     get_color(&expert_color_note);
1744     get_color(&expert_color_warn);
1745     get_color(&expert_color_error);
1746     get_color(&expert_color_foreground);
1747     expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1748     expert_color_note_str = gdk_color_to_string(&expert_color_note);
1749     expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1750     expert_color_error_str = gdk_color_to_string(&expert_color_error);
1751     expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1752
1753     get_color(&hidden_proto_item);
1754
1755     colors_ok = TRUE;
1756 }
1757
1758
1759 static void
1760 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1761                    GtkTreeModel *tree_model, GtkTreeIter *iter,
1762                    gpointer data _U_)
1763 {
1764     field_info   *fi;
1765
1766     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1767
1768     if(!colors_ok) {
1769         proto_draw_colors_init();
1770     }
1771
1772     /* for the various possible attributes, see:
1773      * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1774      *
1775      * color definitions can be found at:
1776      * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1777      * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1778      *
1779      * some experiences:
1780      * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1781      * weight/style: doesn't take any effect
1782      */
1783
1784     /* for each field, we have to reset the renderer attributes */
1785     g_object_set (cell, "foreground-set", FALSE, NULL);
1786
1787     g_object_set (cell, "background-set", FALSE, NULL);
1788
1789     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1790     g_object_set (cell, "underline-set", FALSE, NULL);
1791
1792     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1793     g_object_set (cell, "style-set", FALSE, NULL);*/
1794
1795     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1796     g_object_set (cell, "weight-set", FALSE, NULL);*/
1797
1798     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1799         /* we use "[...]" to mark generated items, no need to change things here */
1800
1801         /* as some fonts don't support italic, don't use this */
1802         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1803         g_object_set (cell, "style-set", TRUE, NULL);
1804         */
1805         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1806         g_object_set (cell, "weight-set", TRUE, NULL);*/
1807     }
1808
1809     if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1810         g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1811         g_object_set (cell, "foreground-set", TRUE, NULL);
1812     }
1813
1814     if (fi && fi->hfinfo) {
1815         if(fi->hfinfo->type == FT_PROTOCOL) {
1816             g_object_set (cell, "background", "gray90", NULL);
1817             g_object_set (cell, "background-set", TRUE, NULL);
1818             g_object_set (cell, "foreground", "black", NULL);
1819             g_object_set (cell, "foreground-set", TRUE, NULL);
1820             /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1821             g_object_set (cell, "weight-set", TRUE, NULL);*/
1822         }
1823
1824         if((fi->hfinfo->type == FT_FRAMENUM) ||
1825            (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1826             render_as_url(cell);
1827         }
1828     }
1829
1830     if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1831         switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1832         case(PI_CHAT):
1833             g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1834             g_object_set (cell, "background-set", TRUE, NULL);
1835             break;
1836         case(PI_NOTE):
1837             g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1838             g_object_set (cell, "background-set", TRUE, NULL);
1839             break;
1840         case(PI_WARN):
1841             g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1842             g_object_set (cell, "background-set", TRUE, NULL);
1843             break;
1844         case(PI_ERROR):
1845             g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1846             g_object_set (cell, "background-set", TRUE, NULL);
1847             break;
1848         default:
1849             g_assert_not_reached();
1850         }
1851         g_object_set (cell, "foreground", "black", NULL);
1852         g_object_set (cell, "foreground-set", TRUE, NULL);
1853     }
1854 }
1855
1856 GtkWidget *
1857 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1858 {
1859     GtkWidget *tv_scrollw, *tree_view;
1860     GtkTreeStore *store;
1861     GtkCellRenderer *renderer;
1862     GtkTreeViewColumn *column;
1863     gint col_offset;
1864
1865     /* Tree view */
1866     tv_scrollw = scrolled_window_new(NULL, NULL);
1867     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1868                                         GTK_SHADOW_IN);
1869
1870     store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1871     tree_view = tree_view_new(GTK_TREE_MODEL(store));
1872     g_object_unref(G_OBJECT(store));
1873     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1874     renderer = gtk_cell_renderer_text_new();
1875     g_object_set (renderer, "ypad", 0, NULL);
1876     col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1877                                                              -1, "Name", renderer,
1878                                                              "text", 0, NULL);
1879     column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1880                                       col_offset - 1);
1881     gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1882                                             NULL, NULL);
1883
1884     gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1885                                     GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1886     g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1887     g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1888     gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1889     set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
1890     gtk_widget_modify_font(tree_view, user_font_get_regular());
1891     remember_ptree_widget(tree_view);
1892
1893     *tree_view_p = tree_view;
1894
1895     return tv_scrollw;
1896 }
1897
1898 void
1899 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1900 {
1901     int i;
1902     for(i=0; i < num_tree_types; i++) {
1903         tree_is_expanded[i] = TRUE;
1904     }
1905     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1906 }
1907
1908 void
1909 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1910 {
1911     int i;
1912     for(i=0; i < num_tree_types; i++) {
1913         tree_is_expanded[i] = FALSE;
1914     }
1915     gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1916 }
1917
1918
1919 struct proto_tree_draw_info {
1920     GtkTreeView  *tree_view;
1921     GtkTreeIter  *iter;
1922 };
1923
1924 void
1925 main_proto_tree_draw(proto_tree *protocol_tree)
1926 {
1927     proto_tree_draw(protocol_tree, tree_view_gbl);
1928 }
1929
1930
1931 static void
1932 tree_view_follow_link(field_info   *fi)
1933 {
1934     gchar *url;
1935
1936     if(fi->hfinfo->type == FT_FRAMENUM) {
1937         cf_goto_frame(&cfile, fi->value.value.uinteger);
1938     }
1939     if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1940         url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1941         browser_open_url(url);
1942         g_free(url);
1943     }
1944 }
1945
1946
1947 /* If the user selected a position in the tree view, try to find
1948  * the item in the GUI proto_tree that corresponds to that byte, and
1949  * select it. */
1950 gboolean
1951 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1952 {
1953     GtkTreeSelection    *sel;
1954     GtkTreePath         *path;
1955
1956     if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1957                                       (gint) (((GdkEventButton *)event)->x),
1958                                       (gint) (((GdkEventButton *)event)->y),
1959                                       &path, NULL, NULL, NULL))
1960     {
1961         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1962
1963         /* if that's a doubleclick, try to follow the link */
1964         if(event->type == GDK_2BUTTON_PRESS) {
1965             GtkTreeModel *model;
1966             GtkTreeIter iter;
1967             field_info   *fi;
1968
1969             if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1970                 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1971                 tree_view_follow_link(fi);
1972             }
1973         }
1974         else if (((GdkEventButton *)event)->button != 1) {
1975             /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1976             gtk_tree_selection_select_path(sel, path);
1977         }
1978     } else {
1979         return FALSE;
1980     }
1981     return TRUE;
1982 }
1983
1984 /* fill the whole protocol tree with the string values */
1985 void
1986 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1987 {
1988     GtkTreeStore *store;
1989     struct proto_tree_draw_info info;
1990
1991     info.tree_view = GTK_TREE_VIEW(tree_view);
1992     info.iter = NULL;
1993
1994     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1995
1996     /*
1997      * Clear out any crud left over in the display of the protocol
1998      * tree, by removing all nodes from the tree.
1999      * This is how it's done in testgtk.c in GTK+.
2000      */
2001     gtk_tree_store_clear(store);
2002
2003     proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2004 }
2005
2006
2007 /* fill a single protocol tree item with the string value */
2008 static void
2009 proto_tree_draw_node(proto_node *node, gpointer data)
2010 {
2011     struct proto_tree_draw_info  info;
2012     struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2013
2014     field_info   *fi = PNODE_FINFO(node);
2015     gchar         label_str[ITEM_LABEL_LENGTH];
2016     gchar        *label_ptr;
2017     gboolean      is_leaf, is_expanded;
2018     GtkTreeStore *store;
2019     GtkTreeIter   iter;
2020     GtkTreePath  *path;
2021
2022     g_assert(fi && "dissection with an invisible proto tree?");
2023
2024     if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2025         return;
2026
2027     /* was a free format label produced? */
2028     if (fi->rep) {
2029         label_ptr = fi->rep->representation;
2030     }
2031     else { /* no, make a generic label */
2032         label_ptr = label_str;
2033         proto_item_fill_label(fi, label_str);
2034     }
2035
2036     if (node->first_child != NULL) {
2037         is_leaf = FALSE;
2038         g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2039         if (tree_is_expanded[fi->tree_type]) {
2040             is_expanded = TRUE;
2041         }
2042         else {
2043             is_expanded = FALSE;
2044         }
2045     }
2046     else {
2047         is_leaf = TRUE;
2048         is_expanded = FALSE;
2049     }
2050
2051     if (PROTO_ITEM_IS_GENERATED(node)) {
2052         if (PROTO_ITEM_IS_HIDDEN(node)) {
2053             label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2054         } else {
2055             label_ptr = g_strdup_printf("[%s]", label_ptr);
2056         }
2057     } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2058         label_ptr = g_strdup_printf("<%s>", label_ptr);
2059     }
2060
2061     info.tree_view = parent_info->tree_view;
2062     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2063     gtk_tree_store_append(store, &iter, parent_info->iter);
2064     gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2065
2066     if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2067         g_free(label_ptr);
2068     }
2069
2070     if (!is_leaf) {
2071         info.iter = &iter;
2072         proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2073         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2074         if (is_expanded)
2075             gtk_tree_view_expand_to_path(info.tree_view, path);
2076         else
2077             gtk_tree_view_collapse_row(info.tree_view, path);
2078         gtk_tree_path_free(path);
2079     }
2080 }
2081
2082 /*
2083  * Clear the hex dump and protocol tree panes.
2084  */
2085 void
2086 clear_tree_and_hex_views(void)
2087 {
2088     /* Clear the hex dump by getting rid of all the byte views. */
2089     while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2090         gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2091
2092     /* Add a placeholder byte view so that there's at least something
2093        displayed in the byte view notebook. */
2094     add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2095
2096     /* Clear the protocol tree by removing all nodes in the ctree.
2097        This is how it's done in testgtk.c in GTK+ */
2098     gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2099 }
2100
2101 void
2102 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2103 {
2104     if (recent.gui_bytes_view != view) {
2105         recent.gui_bytes_view = view;
2106         redraw_packet_bytes_all();
2107     }
2108 }