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