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