The rest of the Follow UDP Stream check-in
[obnox/wireshark/wip.git] / gtk / 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 "isprint.h"
50
51 #include "main.h"
52 #include <epan/packet.h>
53 #include <epan/charsets.h>
54 #include "menu.h"
55 #include "keys.h"
56
57 #include <epan/prefs.h>
58 #include "colors.h"
59 #include "capture_file_dlg.h"
60 #include "proto_draw.h"
61 #include "packet_win.h"
62 #if 0
63 #include "dlg_utils.h"
64 #endif
65 #include "file_dlg.h"
66 #include "gui_utils.h"
67 #include "gtkglobals.h"
68 #include "compat_macros.h"
69 #include "alert_box.h"
70 #include "simple_dialog.h"
71 #include "progress_dlg.h"
72 #include "font_utils.h"
73
74 #include "../ui_util.h"
75 #include "file_util.h"
76 #include "webbrowser.h"
77
78 #if GTK_MAJOR_VERSION >= 2 && _WIN32
79 #include <gdk/gdkwin32.h>
80 #include <windows.h>
81 #include "win32-file-dlg.h"
82 #endif
83
84 #define BYTE_VIEW_WIDTH    16
85 #define BYTE_VIEW_SEP      8
86
87 #define E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
88 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
89 #define E_BYTE_VIEW_NDIGITS_KEY   "byte_view_ndigits"
90 #define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
91 #define E_BYTE_VIEW_START_KEY     "byte_view_start"
92 #define E_BYTE_VIEW_END_KEY       "byte_view_end"
93 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
94 #define E_BYTE_VIEW_APP_END_KEY   "byte_view_app_end"
95 #define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
96
97
98 #if GTK_MAJOR_VERSION < 2
99 GtkStyle *item_style = NULL;
100 #endif
101
102 /* gtk_tree_view_expand_to_path doesn't exist in gtk+ v2.0 so we must include it
103  * when building with this version (taken from gtk+ v2.2.4) */
104 #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION == 0
105 /**
106  * gtk_tree_view_expand_to_path:
107  * @tree_view: A #GtkTreeView.
108  * @path: path to a row.
109  *
110  * Expands the row at @path. This will also expand all parent rows of
111  * @path as necessary.
112  *
113  * Since: 2.2
114  **/
115 void
116 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
117                               GtkTreePath *path)
118 {
119   gint i, depth;
120   gint *indices;
121   GtkTreePath *tmp;
122
123   g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
124   g_return_if_fail (path != NULL);
125
126   depth = gtk_tree_path_get_depth (path);
127   indices = gtk_tree_path_get_indices (path);
128
129   tmp = gtk_tree_path_new ();
130   g_return_if_fail (tmp != NULL);
131
132   for (i = 0; i < depth; i++)
133     {
134       gtk_tree_path_append_index (tmp, indices[i]);
135       gtk_tree_view_expand_row (tree_view, tmp, FALSE);
136     }
137
138   gtk_tree_path_free (tmp);
139 }
140 #endif
141
142 static GtkWidget *
143 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
144     proto_tree *tree, GtkWidget *tree_view);
145
146 static void
147 proto_tree_draw_node(proto_node *node, gpointer data);
148
149 /* Get the current text window for the notebook. */
150 GtkWidget *
151 get_notebook_bv_ptr(GtkWidget *nb_ptr)
152 {
153   int num;
154   GtkWidget *bv_page;
155
156   num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
157   bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
158   if (bv_page)
159       return GTK_BIN(bv_page)->child;
160   else
161       return NULL;
162 }
163
164 /*
165  * Get the data and length for a byte view, given the byte view page.
166  * Return the pointer, or NULL on error, and set "*data_len" to the length.
167  */
168 const guint8 *
169 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
170 {
171   tvbuff_t *byte_view_tvb;
172   const guint8 *data_ptr;
173
174   byte_view_tvb = OBJECT_GET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY);
175   if (byte_view_tvb == NULL)
176     return NULL;
177
178   data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
179   *data_len = tvb_length(byte_view_tvb);
180   return data_ptr;
181 }
182
183 /*
184  * Set the current text window for the notebook to the window that
185  * refers to a particular tvbuff.
186  */
187 void
188 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
189 {
190   int num;
191   GtkWidget *bv_page, *bv;
192   tvbuff_t *bv_tvb;
193
194   for (num = 0;
195        (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
196        num++) {
197     bv = GTK_BIN(bv_page)->child;
198     bv_tvb = OBJECT_GET_DATA(bv, E_BYTE_VIEW_TVBUFF_KEY);
199     if (bv_tvb == tvb) {
200       /* Found it. */
201       gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
202       break;
203     }
204   }
205 }
206
207 /* Redraw a given byte view window. */
208 void
209 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
210 {
211   GtkWidget *bv;
212   const guint8 *data;
213   guint len;
214
215   bv = get_notebook_bv_ptr(nb);
216   if (bv != NULL) {
217     data = get_byte_view_data_and_length(bv, &len);
218     if (data != NULL)
219       packet_hex_print(bv, data, fd, finfo, len);
220   }
221 }
222
223 /* Redraw all byte view windows. */
224 void
225 redraw_hex_dump_all(void)
226 {
227     if (cfile.current_frame != NULL)
228             redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
229
230   redraw_hex_dump_packet_wins();
231
232 #if GTK_MAJOR_VERSION >= 2
233   /* XXX - this is a hack, to workaround a bug in GTK2.x!
234      when changing the font size, even refilling of the corresponding
235      gtk_text_buffer doesn't seem to trigger an update.
236      The only workaround is to freshly select the frame, which will remove any
237      existing notebook tabs and "restart" the whole byte view again. */
238   if (cfile.current_frame != NULL)
239     cf_goto_frame(&cfile, cfile.current_frame->num);
240 #endif
241 }
242
243 #if GTK_MAJOR_VERSION < 2
244 static void
245 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
246 #else
247 static void
248 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
249             GtkTreePath *path _U_, gpointer user_data _U_)
250 #endif
251 {
252     field_info   *finfo;
253 #if GTK_MAJOR_VERSION >= 2
254     GtkTreeModel *model;
255
256     model = gtk_tree_view_get_model(tree_view);
257     gtk_tree_model_get(model, iter, 1, &finfo, -1);
258 #else
259     finfo = gtk_ctree_node_get_row_data( ctree, node);
260 #endif
261     g_assert(finfo);
262
263     /*
264      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
265      * are thus presumably leaf nodes and cannot be expanded.
266      */
267     if (finfo->tree_type != -1) {
268         g_assert(finfo->tree_type >= 0 &&
269                  finfo->tree_type < num_tree_types);
270         tree_is_expanded[finfo->tree_type] = TRUE;
271     }
272 }
273
274 #if GTK_MAJOR_VERSION < 2
275 static void
276 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
277 #else
278 static void
279 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
280             GtkTreePath *path _U_, gpointer user_data _U_)
281 #endif
282 {
283     field_info   *finfo;
284 #if GTK_MAJOR_VERSION >= 2
285     GtkTreeModel *model;
286
287     model = gtk_tree_view_get_model(tree_view);
288     gtk_tree_model_get(model, iter, 1, &finfo, -1);
289 #else
290     finfo = gtk_ctree_node_get_row_data( ctree, node);
291 #endif
292     g_assert(finfo);
293
294     /*
295      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
296      * are thus presumably leaf nodes and cannot be collapsed.
297      */
298     if (finfo->tree_type != -1) {
299         g_assert(finfo->tree_type >= 0 &&
300                  finfo->tree_type < num_tree_types);
301         tree_is_expanded[finfo->tree_type] = FALSE;
302     }
303 }
304
305 #if GTK_MAJOR_VERSION < 2
306 static void
307 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
308 {
309         if (event->keyval != GDK_Return)
310                 return;
311         gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
312 }
313 #endif
314
315 #define MAX_OFFSET_LEN  8       /* max length of hex offset of bytes */
316 #define BYTES_PER_LINE  16      /* max byte values in a line */
317 #define HEX_DUMP_LEN    (BYTES_PER_LINE*3 + 1)
318                                 /* max number of characters hex dump takes -
319                                    2 digits plus trailing blank
320                                    plus separator between first and
321                                    second 8 digits */
322 #define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
323                                 /* number of characters those bytes take;
324                                    3 characters per byte of hex dump,
325                                    2 blanks separating hex from ASCII,
326                                    1 character per byte of ASCII dump */
327 #define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
328                                 /* number of characters per line;
329                                    offset, 2 blanks separating offset
330                                    from data dump, data dump */
331
332 /* Which byte the offset is referring to. Associates
333  * whitespace with the preceding digits. */
334 static int
335 byte_num(int offset, int start_point)
336 {
337         return (offset - start_point) / 3;
338 }
339
340 #if GTK_MAJOR_VERSION >= 2
341 struct field_lookup_info {
342     field_info  *fi;
343     GtkTreeIter  iter;
344 };
345
346 static gboolean
347 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
348             gpointer data)
349 {
350     field_info *fi;
351     struct field_lookup_info *fli = (struct field_lookup_info *)data;
352
353     gtk_tree_model_get(model, iter, 1, &fi, -1);
354     if (fi == fli->fi) {
355         fli->iter = *iter;
356         return TRUE;
357     }
358     return FALSE;
359 }
360
361 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
362   GtkTreeModel *model;
363   struct field_lookup_info fli;
364
365   g_assert(finfo != NULL);
366
367   model = gtk_tree_view_get_model(tree_view);
368   fli.fi = finfo;
369   gtk_tree_model_foreach(model, lookup_finfo, &fli);
370
371   return gtk_tree_model_get_path(model, &fli.iter);
372 }
373
374 #endif
375
376 /* If the user selected a certain byte in the byte view, try to find
377  * the item in the GUI proto_tree that corresponds to that byte, and:
378  *
379  *      if we succeed, select it, and return TRUE;
380  *      if we fail, return FALSE. */
381 gboolean
382 byte_view_select(GtkWidget *widget, GdkEventButton *event)
383 {
384     proto_tree   *tree;
385 #if GTK_MAJOR_VERSION < 2
386     GtkCTree     *ctree;
387     /*GtkCTreeNode *node, *parent;*/
388     GtkText      *bv = GTK_TEXT(widget);
389 #else
390     GtkTreeView  *tree_view;
391     GtkTextView  *bv = GTK_TEXT_VIEW(widget);
392     gint          x, y;
393     GtkTextIter   iter;
394 #endif
395     int           row, column;
396     int           byte;
397     tvbuff_t     *tvb;
398     guint         ndigits;
399     int           digits_start_1;
400     int           digits_end_1;
401     int           digits_start_2;
402     int           digits_end_2;
403     int           text_start_1;
404     int           text_end_1;
405     int           text_start_2;
406     int           text_end_2;
407
408     /*
409      * Get the number of digits of offset being displayed, and
410      * compute the columns of various parts of the display.
411      */
412     ndigits = GPOINTER_TO_UINT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY));
413
414     /*
415      * The column of the first hex digit in the first half.
416      * That starts after "ndigits" digits of offset and two
417      * separating blanks.
418      */
419     digits_start_1 = ndigits + 2;
420
421     /*
422      * The column of the last hex digit in the first half.
423      * There are BYTES_PER_LINE/2 bytes displayed in the first
424      * half; there are 2 characters per byte, plus a separating
425      * blank after all but the last byte's characters.
426      *
427      * Then subtract 1 to get the last column of the first half
428      * rather than the first column after the first half.
429      */
430     digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
431         (BYTES_PER_LINE/2 - 1) - 1;
432
433     /*
434      * The column of the first hex digit in the second half.
435      * Add back the 1 to get the first column after the first
436      * half, and then add 2 for the 2 separating blanks between
437      * the halves.
438      */
439     digits_start_2 = digits_end_1 + 3;
440
441     /*
442      * The column of the last hex digit in the second half.
443      * Add the same value we used to get "digits_end_1" from
444      * "digits_start_1".
445      */
446     digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
447         (BYTES_PER_LINE/2 - 1) - 1;
448
449     /*
450      * The column of the first "text dump" character in the first half.
451      * Add back the 1 to get the first column after the second
452      * half's hex dump, and then add 3 for the 3 separating blanks
453      * between the hex and text dummp.
454      */
455     text_start_1 = digits_end_2 + 4;
456
457     /*
458      * The column of the last "text dump" character in the first half.
459      * There are BYTES_PER_LINE/2 bytes displayed in the first
460      * half; there is 1 character per byte.
461      *
462      * Then subtract 1 to get the last column of the first half
463      * rather than the first column after the first half.
464      */
465     text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
466
467     /*
468      * The column of the first "text dump" character in the second half.
469      * Add back the 1 to get the first column after the first half,
470      * and then add 1 for the separating blank between the halves.
471      */
472     text_start_2 = text_end_1 + 2;
473
474     /*
475      * The column of the last "text dump" character in second half.
476      * Add the same value we used to get "text_end_1" from
477      * "text_start_1".
478      */
479     text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
480
481     tree = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_PTR);
482     if (tree == NULL) {
483         /*
484          * Somebody clicked on the dummy byte view; do nothing.
485          */
486         return FALSE;
487     }
488 #if GTK_MAJOR_VERSION < 2
489     ctree = GTK_CTREE(OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_VIEW_PTR));
490 #else
491     tree_view = GTK_TREE_VIEW(OBJECT_GET_DATA(widget,
492                                               E_BYTE_VIEW_TREE_VIEW_PTR));
493 #endif
494
495 #if GTK_MAJOR_VERSION < 2
496     /* Given the mouse (x,y) and the current GtkText (h,v)
497      * adjustments, and the size of the font, figure out
498      * which text column/row the user selected. This could be off
499      * if the bold version of the font is bigger than the
500      * regular version of the font. */
501     column = (int) ((bv->hadj->value + event->x) / user_font_get_regular_width());
502     row = (int) ((bv->vadj->value + event->y) / user_font_get_regular_height());
503 #else
504     /* get the row/column selected */
505     gtk_text_view_window_to_buffer_coords(bv,
506                          gtk_text_view_get_window_type(bv, event->window),
507                          (gint) event->x, (gint) event->y, &x, &y);
508     gtk_text_view_get_iter_at_location(bv, &iter, x, y);
509     row = gtk_text_iter_get_line(&iter);
510     column = gtk_text_iter_get_line_offset(&iter);
511 #endif
512
513     /* Given the column and row, determine which byte offset
514      * the user clicked on. */
515     if (column >= digits_start_1 && column <= digits_end_1) {
516         byte = byte_num(column, digits_start_1);
517         if (byte == -1) {
518             return FALSE;
519         }
520     }
521     else if (column >= digits_start_2 && column <= digits_end_2) {
522         byte = byte_num(column, digits_start_2);
523         if (byte == -1) {
524             return FALSE;
525         }
526         byte += 8;
527     }
528     else if (column >= text_start_1 && column <= text_end_1) {
529         byte = column - text_start_1;
530     }
531     else if (column >= text_start_2 && column <= text_end_2) {
532         byte = 8 + column - text_start_2;
533     }
534     else {
535         /* The user didn't select a hex digit or
536          * text-dump character. */
537         return FALSE;
538     }
539
540     /* Add the number of bytes from the previous rows. */
541     byte += row * 16;
542
543     /* Get the data source tvbuff */
544     tvb = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TVBUFF_KEY);
545
546 #if GTK_MAJOR_VERSION < 2
547     return highlight_field(tvb, byte, ctree, tree);
548 #else
549     return highlight_field(tvb, byte, tree_view, tree);
550 #endif
551 }
552
553 /* This highlights the field in the proto tree that is at position byte */
554 #if GTK_MAJOR_VERSION < 2
555 gboolean
556 highlight_field(tvbuff_t *tvb, gint byte, GtkCTree *ctree,
557                 proto_tree *tree)
558 #else
559 gboolean
560 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
561                 proto_tree *tree)
562 #endif
563 {
564 #if GTK_MAJOR_VERSION < 2
565     GtkCTreeNode *node, *parent;
566 #else
567     GtkTreeModel *model;
568     GtkTreePath  *first_path, *path;
569     GtkTreeIter   parent;
570     struct field_lookup_info fli;
571 #endif
572     field_info   *finfo;
573
574     /* Find the finfo that corresponds to our byte. */
575     finfo = proto_find_field_from_offset(tree, byte, tvb);
576
577     if (!finfo) {
578         return FALSE;
579     }
580
581 #if GTK_MAJOR_VERSION < 2
582     node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
583     g_assert(node);
584
585     /* Expand and select our field's row */
586     gtk_ctree_expand(ctree, node);
587     gtk_ctree_select(ctree, node);
588     expand_tree(ctree, node, NULL);
589
590     /* ... and its parents */
591     parent = GTK_CTREE_ROW(node)->parent;
592     while (parent) {
593         gtk_ctree_expand(ctree, parent);
594         expand_tree(ctree, parent, NULL);
595         parent = GTK_CTREE_ROW(parent)->parent;
596     }
597
598     /* And position the window so the selection is visible.
599      * Position the selection in the middle of the viewable
600      * pane. */
601     gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
602
603     return FALSE;
604 #else
605     model = gtk_tree_view_get_model(tree_view);
606     fli.fi = finfo;
607     gtk_tree_model_foreach(model, lookup_finfo, &fli);
608
609     /* Expand our field's row */
610     first_path = gtk_tree_model_get_path(model, &fli.iter);
611     gtk_tree_view_expand_row(tree_view, first_path, FALSE);
612     expand_tree(tree_view, &fli.iter, NULL, NULL);
613
614     /* ... and its parents */
615     while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
616         path = gtk_tree_model_get_path(model, &parent);
617         gtk_tree_view_expand_row(tree_view, path, FALSE);
618         expand_tree(tree_view, &parent, NULL, NULL);
619         fli.iter = parent;
620         gtk_tree_path_free(path);
621     }
622
623     /* select our field's row */
624     gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
625                                    first_path);
626
627     /* And position the window so the selection is visible.
628      * Position the selection in the middle of the viewable
629      * pane. */
630     gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
631
632     gtk_tree_path_free(first_path);
633
634     return TRUE;
635 #endif
636 }
637
638 /* Calls functions for different mouse-button presses. */
639 static gint
640 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
641 {
642         GdkEventButton *event_button = NULL;
643
644         if(widget == NULL || event == NULL || data == NULL) {
645                 return FALSE;
646         }
647
648         if(event->type == GDK_BUTTON_PRESS) {
649                 event_button = (GdkEventButton *) event;
650
651         /* To qoute the "Gdk Event Structures" doc:
652          * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
653                 switch(event_button->button) {
654
655                 case 1:
656                         return byte_view_select(widget, event_button);
657                 case 3:
658                         return popup_menu_handler(widget, event, data);
659                 default:
660                         return FALSE;
661                 }
662         }
663
664         return FALSE;
665 }
666
667 GtkWidget *
668 byte_view_new(void)
669 {
670   GtkWidget *byte_nb;
671
672   byte_nb = gtk_notebook_new();
673   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
674
675   /* this will only have an effect, if no tabs are shown */
676   gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
677
678   /* set the tabs scrollable, if they don't fit into the pane */
679   gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
680
681   /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
682   gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
683
684   /* Add a placeholder byte view so that there's at least something
685      displayed in the byte view notebook. */
686   add_byte_tab(byte_nb, "", NULL, NULL, NULL);
687
688   return byte_nb;
689 }
690
691 static void
692 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
693 {
694     const guint8 *byte_data;
695     guint byte_len;
696
697     byte_data = get_byte_view_data_and_length(bv, &byte_len);
698     if (byte_data == NULL) {
699         /* This must be the dummy byte view if no packet is selected. */
700         return;
701     }
702     packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
703 }
704
705 static GtkWidget *
706 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
707              proto_tree *tree, GtkWidget *tree_view)
708 {
709   GtkWidget *byte_view, *byte_scrollw, *label;
710 #if GTK_MAJOR_VERSION >= 2
711   GtkTextBuffer *buf;
712   GtkStyle      *style;
713 #endif
714
715   /* Byte view.  Create a scrolled window for the text. */
716   byte_scrollw = scrolled_window_new(NULL, NULL);
717 #if GTK_MAJOR_VERSION < 2
718   /* The horizontal scrollbar of the scroll-window doesn't seem
719    * to affect the GtkText widget at all, even when line wrapping
720    * is turned off in the GtkText widget and there is indeed more
721    * horizontal data. */
722   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
723                         /* Horizontal */GTK_POLICY_NEVER,
724                         /* Vertical*/   GTK_POLICY_ALWAYS);
725 #else
726   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
727                                    GTK_SHADOW_IN);
728 #endif
729   /* Add scrolled pane to tabbed window */
730   label = gtk_label_new(name);
731   gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
732
733   gtk_widget_show(byte_scrollw);
734
735 #if GTK_MAJOR_VERSION < 2
736   byte_view = gtk_text_new(NULL, NULL);
737   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
738   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
739   gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
740 #else
741   byte_view = gtk_text_view_new();
742   gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
743   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
744   buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
745   style = gtk_widget_get_style(GTK_WIDGET(byte_view));
746   gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
747   gtk_text_buffer_create_tag(buf, "reverse",
748                              "font-desc", user_font_get_regular(),
749                              "foreground-gdk", &style->text[GTK_STATE_SELECTED],
750                              "background-gdk", &style->base[GTK_STATE_SELECTED],
751                              NULL);
752   gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
753 #endif
754   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, tvb);
755   gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
756
757   SIGNAL_CONNECT(byte_view, "show", byte_view_realize_cb, NULL);
758   SIGNAL_CONNECT(byte_view, "button_press_event", byte_view_button_press_cb,
759                  OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
760
761   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, tree);
762   OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
763
764   gtk_widget_show(byte_view);
765
766   /* no tabs if this is the first page */
767   if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
768         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
769   else
770         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
771
772   /* set this page (this will print the packet data) */
773   gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb),
774     gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
775
776   return byte_view;
777 }
778
779 void
780 add_main_byte_views(epan_dissect_t *edt)
781 {
782     add_byte_views(edt, tree_view, byte_nb_ptr);
783 }
784
785 void
786 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
787                GtkWidget *byte_nb_ptr)
788 {
789         GSList *src_le;
790         data_source *src;
791
792         /*
793          * Get rid of all the old notebook tabs.
794          */
795         while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
796                 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
797
798         /*
799          * Add to the specified byte view notebook tabs for hex dumps
800          * of all the data sources for the specified frame.
801          */
802         for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
803                 src = src_le->data;
804                 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
805                              tree_view);
806         }
807
808         /*
809          * Initially select the first byte view.
810          */
811         gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
812 }
813
814
815
816 static GtkWidget *savehex_dlg=NULL;
817
818 static void
819 savehex_dlg_destroy_cb(void)
820 {
821         savehex_dlg = NULL;
822 }
823
824
825 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
826 {
827     const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
828     int i, j;
829     gboolean end_of_line = TRUE; /* Initial state is end of line */
830     int byte_line_part_length;
831
832     GString* hex_str;
833     GString* char_str;
834
835     /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
836     hex_str = g_string_new("");
837     char_str= g_string_new("");
838
839     i = 0;
840         while (i<data_len){
841         if(end_of_line) {
842             g_string_sprintfa(hex_str,"%04x  ",i); /* Offset - note that we _append_ here */
843         }
844
845         g_string_sprintfa(hex_str," %02x",*data_p);
846         if(append_text) {
847             g_string_sprintfa(char_str,"%c",isprint(*data_p) ? *data_p : '.');
848         }
849
850         ++data_p;
851
852         /* Look ahead to see if this is the end of the data */
853         byte_line_part_length = (++i) % byte_line_length;
854         if(i == data_len){
855             /* End of data - need to fill in spaces in hex string and then do "end of line".
856              * 
857              */
858             for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
859                 g_string_append(hex_str,"   "); /* Three spaces for each missing byte */
860             }
861             end_of_line = TRUE;
862         } else {
863             end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
864         }
865         
866
867         if (end_of_line){
868             /* End of line */
869             g_string_append(copy_buffer, hex_str->str);
870             if(append_text) {
871                 /* Two spaces between hex and text */
872                 g_string_append_c(copy_buffer, ' ');
873                 g_string_append_c(copy_buffer, ' ');
874                 g_string_append(copy_buffer, char_str->str);
875             }
876             /* Setup ready for next line */
877             g_string_assign(char_str,"");
878             g_string_assign(hex_str, "\n");
879         }
880         }
881
882         g_string_free(hex_str, TRUE);
883         g_string_free(char_str, TRUE);
884 }
885
886 static 
887 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
888 {
889
890     gchar to_append;
891
892     if(isprint(*data_p)) {
893         to_append = *data_p;
894     } else if(*data_p==0x0a) {
895         to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
896     } else {
897         return 1; /* Just ignore non-printable bytes */
898     }
899     g_string_append_c(copy_buffer,to_append);
900     return 1;
901 }
902
903 static 
904 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
905 {
906     g_string_sprintfa(copy_buffer, "%02x", *data_p);
907     return 1;
908 }
909
910 void
911 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
912 {
913         GtkWidget *bv;
914
915     guint len = 0;
916     int bytes_consumed = 0;
917     int flags;
918
919     const guint8* data_p;
920
921         GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
922
923         bv = get_notebook_bv_ptr(byte_nb_ptr);
924         if (bv == NULL) {
925                 /* shouldn't happen */
926                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
927                 return;
928         }
929
930         data_p = get_byte_view_data_and_length(bv, &len);
931         g_assert(data_p != NULL);
932
933     flags = data_type & CD_FLAGSMASK;
934     data_type = data_type & CD_TYPEMASK;
935
936     if(flags & CD_FLAGS_SELECTEDONLY) {
937         int start, end;
938         
939         /* Get the start and end of the highlighted bytes.
940          * XXX The keys appear to be REVERSED start <-> end throughout this file!
941          * Should this be fixed? There is one exception - packet_hex_reprint,
942          * so can't just change it round without functional change.
943          */
944             end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
945             start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
946
947         if(start >= 0 && end > start && (end - start <= (int)len)) {
948             len = end - start;
949             data_p += start;
950         }
951     }
952
953     switch(data_type) {
954     case(CD_ALLINFO):
955         /* This is too different from other text formats - handle separately */
956         copy_hex_all_info(copy_buffer, data_p, len, TRUE);
957         break;
958     case(CD_HEXCOLUMNS):
959         /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
960         copy_hex_all_info(copy_buffer, data_p, len, FALSE);
961         break;
962 #if (GTK_MAJOR_VERSION >= 2)
963     case(CD_BINARY):
964         /* Completely different logic to text copies - leave copy buffer alone */
965         copy_binary_to_clipboard(data_p,len);
966         break;
967 #endif
968     default:
969         /* Incrementally write to text buffer in various formats */
970             while (len > 0){
971             switch(data_type) {
972             case (CD_TEXTONLY):
973                 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
974                 break;
975             case (CD_HEX):
976                 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
977                 break;
978             default:
979                 g_assert_not_reached();
980                 break;
981             }
982
983             g_assert(bytes_consumed>0);
984             data_p += bytes_consumed;
985             len -= bytes_consumed;
986         }
987         break;
988     }
989     
990     if(copy_buffer->len > 0) {
991         copy_to_clipboard(copy_buffer);
992     }
993
994         g_string_free(copy_buffer, TRUE);  
995 }
996
997 /* save the current highlighted hex data */
998 static void
999 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
1000 {
1001         GtkWidget *bv;
1002         int fd, start, end;
1003         guint len;
1004         const guint8 *data_p = NULL;
1005         const char *file = NULL;
1006
1007 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1008         file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
1009 #else
1010         file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(savehex_dlg));
1011 #endif
1012
1013         if (!file ||! *file) {
1014                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
1015                 return;
1016         }
1017
1018         /* Must check if file name exists first */
1019
1020         bv = get_notebook_bv_ptr(byte_nb_ptr);
1021         if (bv == NULL) {
1022                 /* shouldn't happen */
1023                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1024                 return;
1025         }
1026         /*
1027          * Retrieve the info we need
1028          */
1029         end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1030         start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1031         data_p = get_byte_view_data_and_length(bv, &len);
1032
1033         if (data_p == NULL || start == -1 || start > end) {
1034                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1035                     "No data selected to save!");
1036                 return;
1037         }
1038
1039         fd = eth_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
1040         if (fd == -1) {
1041                 open_failure_alert_box(file, errno, TRUE);
1042                 return;
1043         }
1044         if (eth_write(fd, data_p + start, end - start) < 0) {
1045                 write_failure_alert_box(file, errno);
1046                 eth_close(fd);
1047                 return;
1048         }
1049         if (eth_close(fd) < 0) {
1050                 write_failure_alert_box(file, errno);
1051                 return;
1052         }
1053
1054         /* Get rid of the dialog box */
1055         window_destroy(GTK_WIDGET(savehex_dlg));
1056 }
1057
1058 /* Launch the dialog box to put up the file selection box etc */
1059 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1060 {
1061         int start, end;
1062         guint len;
1063         const guint8 *data_p = NULL;
1064         gchar *label;
1065
1066         GtkWidget   *bv;
1067         GtkWidget   *dlg_lb;
1068
1069 #if GTK_MAJOR_VERSION >= 2 && _WIN32
1070         win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
1071         return;
1072 #endif
1073
1074     /* don't show up the dialog, if no data has to be saved */
1075         bv = get_notebook_bv_ptr(byte_nb_ptr);
1076         if (bv == NULL) {
1077                 /* shouldn't happen */
1078                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1079                 return;
1080         }
1081         end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1082         start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1083         data_p = get_byte_view_data_and_length(bv, &len);
1084
1085         if (data_p == NULL || start == -1 || start > end) {
1086                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1087                 return;
1088         }
1089
1090     /* if the window is already open, bring it to front */
1091         if(savehex_dlg){
1092                 reactivate_window(savehex_dlg);
1093                 return;
1094         }
1095
1096         /*
1097          * Build the dialog box we need.
1098          */
1099     savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1100
1101     /* label */
1102     label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1103         end - start, plurality(end - start, "byte", "bytes"));
1104     dlg_lb = gtk_label_new(label);
1105     g_free(label);
1106     file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1107         gtk_widget_show(dlg_lb);
1108
1109     SIGNAL_CONNECT(savehex_dlg, "destroy", savehex_dlg_destroy_cb, NULL);
1110
1111 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1112     if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1113         savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1114     } else {
1115         window_destroy(savehex_dlg);
1116     }
1117 #else
1118     /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1119     pointer to the file selection box widget */
1120     SIGNAL_CONNECT(GTK_FILE_SELECTION (savehex_dlg)->ok_button, "clicked",
1121         savehex_save_clicked_cb, savehex_dlg);
1122
1123     window_set_cancel_button(savehex_dlg,
1124     GTK_FILE_SELECTION(savehex_dlg)->cancel_button, window_cancel_button_cb);
1125
1126     SIGNAL_CONNECT(savehex_dlg, "delete_event", window_delete_event_cb, NULL);
1127
1128     gtk_file_selection_set_filename(GTK_FILE_SELECTION(savehex_dlg), "");
1129
1130     gtk_widget_show_all(savehex_dlg);
1131     window_present(savehex_dlg);
1132 #endif
1133 }
1134
1135
1136
1137 /* Update the progress bar this many times when reading a file. */
1138 #define N_PROGBAR_UPDATES       100
1139
1140
1141 /*
1142  * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1143  * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1144  * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3.  (That's
1145  * presumably why there's a progress bar for it.)
1146  *
1147  * Perhaps what's needed is a custom widget (either one that lets you stuff
1148  * text into it more quickly, or one that's a "virtual" widget so that the
1149  * text for a row is constructed, via a callback, when the row is to be
1150  * displayed).  A custom widget might also let us treat the offset, hex
1151  * data, and ASCII data as three columns, so you can select purely in
1152  * the hex dump column.
1153  */
1154 static void
1155 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1156                         int bend, int astart, int aend, int encoding)
1157 {
1158   int            i = 0, j, k, cur;
1159   guchar         line[MAX_LINE_LEN + 1];
1160   static guchar  hexchars[16] = {
1161       '0', '1', '2', '3', '4', '5', '6', '7',
1162       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1163   guchar         c = '\0';
1164   unsigned int   use_digits;
1165   gboolean       reverse, newreverse;
1166 #if GTK_MAJOR_VERSION < 2
1167   GdkFont       *cur_font, *new_font;
1168   GdkColor      *fg, *bg;
1169   GtkText       *bv_text = GTK_TEXT(bv);
1170 #else
1171   GtkTextView   *bv_text_view = GTK_TEXT_VIEW(bv);
1172   GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1173   GtkTextIter    iter;
1174   const char    *revstyle;
1175   gchar         *convline;
1176   gsize          newsize;
1177   GtkTextMark   *mark = NULL;
1178 #endif
1179
1180   progdlg_t  *progbar = NULL;
1181   float       progbar_val;
1182   gboolean    progbar_stop_flag;
1183   GTimeVal    progbar_start_time;
1184   gchar       progbar_status_str[100];
1185   int         progbar_nextstep;
1186   int         progbar_quantum;
1187
1188 #if GTK_MAJOR_VERSION < 2
1189   /* Freeze the text for faster display */
1190   gtk_text_freeze(bv_text);
1191
1192   /* Clear out the text */
1193   gtk_text_set_point(bv_text, 0);
1194   /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
1195      http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
1196      http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
1197      for more information */
1198   gtk_adjustment_set_value(bv_text->vadj, 0.0);
1199   gtk_text_forward_delete(bv_text, gtk_text_get_length(bv_text));
1200 #else
1201   gtk_text_buffer_set_text(buf, "", 0);
1202   gtk_text_buffer_get_start_iter(buf, &iter);
1203 #endif
1204
1205   /*
1206    * How many of the leading digits of the offset will we supply?
1207    * We always supply at least 4 digits, but if the maximum offset
1208    * won't fit in 4 digits, we use as many digits as will be needed.
1209    */
1210   if (((len - 1) & 0xF0000000) != 0)
1211     use_digits = 8;     /* need all 8 digits */
1212   else if (((len - 1) & 0x0F000000) != 0)
1213     use_digits = 7;     /* need 7 digits */
1214   else if (((len - 1) & 0x00F00000) != 0)
1215     use_digits = 6;     /* need 6 digits */
1216   else if (((len - 1) & 0x000F0000) != 0)
1217     use_digits = 5;     /* need 5 digits */
1218   else
1219     use_digits = 4;     /* we'll supply 4 digits */
1220
1221   /* Record the number of digits in this text view. */
1222   OBJECT_SET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1223
1224   /* Update the progress bar when it gets to this value. */
1225   progbar_nextstep = 0;
1226   /* When we reach the value that triggers a progress bar update,
1227      bump that value by this amount. */
1228   progbar_quantum = len/N_PROGBAR_UPDATES;
1229   /* Progress so far. */
1230   progbar_val = 0.0;
1231
1232   progbar_stop_flag = FALSE;
1233   g_get_current_time(&progbar_start_time);
1234
1235   while (i < len) {
1236     /* Create the progress bar if necessary.
1237        We check on every iteration of the loop, so that it takes no
1238        longer than the standard time to create it (otherwise, for a
1239        large packet, we might take considerably longer than that standard
1240        time in order to get to the next progress bar step). */
1241     if (progbar == NULL)
1242       progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1243                                             TRUE,
1244                                             &progbar_stop_flag,
1245                                             &progbar_start_time,
1246                                             progbar_val);
1247
1248     /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1249        when we update it, we have to run the GTK+ main loop to get it
1250        to repaint what's pending, and doing so may involve an "ioctl()"
1251        to see if there's any pending input from an X server, and doing
1252        that for every packet can be costly, especially on a big file. */
1253     if (i >= progbar_nextstep) {
1254       /* let's not divide by zero. I should never be started
1255        * with count == 0, so let's assert that
1256        */
1257       g_assert(len > 0);
1258       progbar_val = (gfloat) i / len;
1259
1260       if (progbar != NULL) {
1261         g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1262                    "%4u of %u bytes", i, len);
1263         update_progress_dlg(progbar, progbar_val, progbar_status_str);
1264       }
1265
1266       progbar_nextstep += progbar_quantum;
1267     }
1268
1269     if (progbar_stop_flag) {
1270       /* Well, the user decided to abort the operation.  Just stop,
1271          and arrange to return TRUE to our caller, so they know it
1272          was stopped explicitly. */
1273       break;
1274     }
1275
1276     /* Print the line number */
1277     j = use_digits;
1278     cur = 0;
1279     do {
1280       j--;
1281       c = (i >> (j*4)) & 0xF;
1282       line[cur++] = hexchars[c];
1283     } while (j != 0);
1284     line[cur++] = ' ';
1285     line[cur++] = ' ';
1286     line[cur] = '\0';
1287
1288     /* Display with inverse video ? */
1289 #if GTK_MAJOR_VERSION < 2
1290     if (prefs.gui_hex_dump_highlight_style) {
1291       gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1292       /* Do we start in reverse? */
1293       reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1294       fg = reverse ? &WHITE : &BLACK;
1295       bg = reverse ? &BLACK : &WHITE;
1296       j   = i;
1297       k   = i + BYTE_VIEW_WIDTH;
1298       cur = 0;
1299       /* Print the hex bit */
1300       while (i < k) {
1301         if (i < len) {
1302           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1303           line[cur++] = hexchars[pd[i] & 0x0f];
1304         } else {
1305           line[cur++] = ' '; line[cur++] = ' ';
1306         }
1307         i++;
1308         newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1309         /* Have we gone from reverse to plain? */
1310         if (reverse && (reverse != newreverse)) {
1311           gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1312           fg = &BLACK;
1313           bg = &WHITE;
1314           cur = 0;
1315         }
1316         /* Inter byte space if not at end of line */
1317         if (i < k) {
1318           line[cur++] = ' ';
1319           /* insert a space every BYTE_VIEW_SEP bytes */
1320           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1321             line[cur++] = ' ';
1322           }
1323         }
1324         /* Have we gone from plain to reversed? */
1325         if (!reverse && (reverse != newreverse)) {
1326           gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1327           fg = &WHITE;
1328           bg = &BLACK;
1329           cur = 0;
1330         }
1331         reverse = newreverse;
1332       }
1333       /* Print remaining part of line */
1334       gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1335       cur = 0;
1336       /* Print some space at the end of the line */
1337       line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1338       gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, cur);
1339       cur = 0;
1340
1341       /* Print the ASCII bit */
1342       i = j;
1343       /* Do we start in reverse? */
1344       reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1345       fg = reverse ? &WHITE : &BLACK;
1346       bg = reverse ? &BLACK : &WHITE;
1347       while (i < k) {
1348         if (i < len) {
1349           if (encoding == CHAR_ASCII) {
1350             c = pd[i];
1351           }
1352           else if (encoding == CHAR_EBCDIC) {
1353             c = EBCDIC_to_ASCII1(pd[i]);
1354           }
1355           else {
1356                   g_assert_not_reached();
1357           }
1358           line[cur++] = isprint(c) ? c : '.';
1359         } else {
1360           line[cur++] = ' ';
1361         }
1362         i++;
1363         newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1364         /* Have we gone from reverse to plain? */
1365         if (reverse && (reverse != newreverse)) {
1366           gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1367           fg = &BLACK;
1368           bg = &WHITE;
1369           cur = 0;
1370         }
1371         if (i < k) {
1372           /* insert a space every BYTE_VIEW_SEP bytes */
1373           if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1374             line[cur++] = ' ';
1375           }
1376         }
1377         /* Have we gone from plain to reversed? */
1378         if (!reverse && (reverse != newreverse)) {
1379           gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1380           fg = &WHITE;
1381           bg = &BLACK;
1382           cur = 0;
1383         }
1384         reverse = newreverse;
1385       }
1386       /* Print remaining part of line */
1387       gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1388       cur = 0;
1389       line[cur++] = '\n';
1390       line[cur]   = '\0';
1391       gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1392     }
1393     else {
1394       gtk_text_insert(bv_text, user_font_get_regular(), NULL, NULL, line, -1);
1395       /* Do we start in bold? */
1396       cur_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1397       j   = i;
1398       k   = i + BYTE_VIEW_WIDTH;
1399       cur = 0;
1400       /* Print the hex bit */
1401       while (i < k) {
1402         if (i < len) {
1403           line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1404           line[cur++] = hexchars[pd[i] & 0x0f];
1405         } else {
1406           line[cur++] = ' '; line[cur++] = ' ';
1407         }
1408         line[cur++] = ' ';
1409         i++;
1410         /* insert a space every BYTE_VIEW_SEP bytes */
1411         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1412         /* Did we cross a bold/plain boundary? */
1413         new_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1414         if (cur_font != new_font) {
1415           gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1416           cur_font = new_font;
1417           cur = 0;
1418         }
1419       }
1420       line[cur++] = ' ';
1421       gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1422
1423       cur = 0;
1424       i = j;
1425       /* Print the ASCII bit */
1426       cur_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1427       while (i < k) {
1428         if (i < len) {
1429           if (encoding == CHAR_ASCII) {
1430             c = pd[i];
1431           }
1432           else if (encoding == CHAR_EBCDIC) {
1433             c = EBCDIC_to_ASCII1(pd[i]);
1434           }
1435           else {
1436                   g_assert_not_reached();
1437           }
1438           line[cur++] = isprint(c) ? c : '.';
1439         } else {
1440           line[cur++] = ' ';
1441         }
1442         i++;
1443         /* insert a space every BYTE_VIEW_SEP bytes */
1444         if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1445         /* Did we cross a bold/plain boundary? */
1446         new_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1447         if (cur_font != new_font) {
1448           gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1449           cur_font = new_font;
1450           cur = 0;
1451         }
1452       }
1453       line[cur++] = '\n';
1454       line[cur]   = '\0';
1455       gtk_text_insert(bv_text, cur_font, NULL, NULL, line, -1);
1456     }
1457 #else
1458     if (prefs.gui_hex_dump_highlight_style)
1459       revstyle = "reverse";
1460     else
1461       revstyle = "bold";
1462
1463     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1,
1464                                              "plain", NULL);
1465     /* Do we start in reverse? */
1466     reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1467     j   = i;
1468     k   = i + BYTE_VIEW_WIDTH;
1469     cur = 0;
1470     /* Print the hex bit */
1471     while (i < k) {
1472       if (i < len) {
1473         line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1474         line[cur++] = hexchars[pd[i] & 0x0f];
1475       } else {
1476         line[cur++] = ' '; line[cur++] = ' ';
1477       }
1478       i++;
1479       newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1480       /* Have we gone from reverse to plain? */
1481       if (reverse && (reverse != newreverse)) {
1482         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1483                                                  revstyle, NULL);
1484         cur = 0;
1485       }
1486       /* Inter byte space if not at end of line */
1487       if (i < k) {
1488         line[cur++] = ' ';
1489         /* insert a space every BYTE_VIEW_SEP bytes */
1490         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1491           line[cur++] = ' ';
1492         }
1493       }
1494       /* Have we gone from plain to reversed? */
1495       if (!reverse && (reverse != newreverse)) {
1496         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1497                                                  "plain", NULL);
1498         mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1499         cur = 0;
1500       }
1501       reverse = newreverse;
1502     }
1503     /* Print remaining part of line */
1504     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1505                                              reverse ? revstyle : "plain",
1506                                              NULL);
1507     cur = 0;
1508     /* Print some space at the end of the line */
1509     line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1510     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1511                                              "plain", NULL);
1512     cur = 0;
1513
1514     /* Print the ASCII bit */
1515     i = j;
1516     /* Do we start in reverse? */
1517     reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1518     while (i < k) {
1519       if (i < len) {
1520         if (encoding == CHAR_ASCII) {
1521           c = pd[i];
1522         }
1523         else if (encoding == CHAR_EBCDIC) {
1524           c = EBCDIC_to_ASCII1(pd[i]);
1525         }
1526         else {
1527           g_assert_not_reached();
1528         }
1529         line[cur++] = isprint(c) ? c : '.';
1530       } else {
1531         line[cur++] = ' ';
1532       }
1533       i++;
1534       newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1535       /* Have we gone from reverse to plain? */
1536       if (reverse && (reverse != newreverse)) {
1537         convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1538         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1539                                                  revstyle, NULL);
1540         g_free( (gpointer) convline);
1541         cur = 0;
1542       }
1543       if (i < k) {
1544         /* insert a space every BYTE_VIEW_SEP bytes */
1545         if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1546           line[cur++] = ' ';
1547         }
1548       }
1549       /* Have we gone from plain to reversed? */
1550       if (!reverse && (reverse != newreverse)) {
1551         convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1552         gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1553                                                  "plain", NULL);
1554         g_free( (gpointer) convline);
1555         cur = 0;
1556       }
1557       reverse = newreverse;
1558     }
1559     /* Print remaining part of line */
1560     convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1561     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1562                                              reverse ? revstyle : "plain",
1563                                              NULL);
1564     g_free( (gpointer) convline);
1565     cur = 0;
1566     line[cur++] = '\n';
1567     gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1568                                              "plain", NULL);
1569 #endif
1570   }
1571
1572   /* We're done printing the packets; destroy the progress bar if
1573      it was created. */
1574   if (progbar != NULL)
1575     destroy_progress_dlg(progbar);
1576
1577   /* scroll text into position */
1578 #if GTK_MAJOR_VERSION < 2
1579   gtk_text_thaw(bv_text); /* must thaw before adjusting scroll bars */
1580   if ( bstart > 0 ) {
1581     int linenum;
1582     float scrollval;
1583
1584     linenum = bstart / BYTE_VIEW_WIDTH;
1585     scrollval = MIN(linenum * user_font_get_regular_height(),
1586                     bv_text->vadj->upper - bv_text->vadj->page_size);
1587
1588     gtk_adjustment_set_value(bv_text->vadj, scrollval);
1589   }
1590 #else
1591   if (mark) {
1592     gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1593     gtk_text_buffer_delete_mark(buf, mark);
1594   }
1595 #endif
1596 }
1597
1598 void
1599 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1600                  field_info *finfo, guint len)
1601 {
1602   /* do the initial printing and save the information needed    */
1603   /* to redraw the display if preferences change.               */
1604
1605   int bstart = -1, bend = -1, blen = -1;
1606   int astart = -1, aend = -1, alen = -1;
1607
1608   if (finfo != NULL) {
1609     bstart = finfo->start;
1610     blen = finfo->length;
1611     astart = finfo->appendix_start;
1612     alen = finfo->appendix_length;
1613   }
1614   if (bstart >= 0 && blen >= 0) {
1615     bend = bstart + blen;
1616   }
1617   if (astart >= 0 && alen >= 0) {
1618     aend = astart + alen;
1619   }
1620
1621   /* save the information needed to redraw the text */
1622   /* should we save the fd & finfo pointers instead ?? */
1623   OBJECT_SET_DATA(bv, E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1624   OBJECT_SET_DATA(bv, E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1625   OBJECT_SET_DATA(bv, E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1626   OBJECT_SET_DATA(bv, E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1627   OBJECT_SET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY,
1628                   GUINT_TO_POINTER((guint)fd->flags.encoding));
1629
1630   packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1631 }
1632
1633 /*
1634  * Redraw the text using the saved information; usually called if
1635  * the preferences have changed.
1636  */
1637 void
1638 packet_hex_reprint(GtkWidget *bv)
1639 {
1640   int start, end, encoding;
1641   int astart, aend;
1642   const guint8 *data;
1643   guint len = 0;
1644
1645   start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1646   end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1647   astart = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_APP_START_KEY));
1648   aend = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_APP_END_KEY));
1649   data = get_byte_view_data_and_length(bv, &len);
1650   g_assert(data != NULL);
1651   encoding = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY));
1652
1653   packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1654 }
1655
1656 /* List of all protocol tree widgets, so we can globally set the selection
1657    mode and font of all of them. */
1658 static GList *ptree_widgets;
1659
1660 /* Add a protocol tree widget to the list of protocol tree widgets. */
1661 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1662
1663 static void
1664 remember_ptree_widget(GtkWidget *ptreew)
1665 {
1666   ptree_widgets = g_list_append(ptree_widgets, ptreew);
1667
1668   /* Catch the "destroy" event on the widget, so that we remove it from
1669      the list when it's destroyed. */
1670   SIGNAL_CONNECT(ptreew, "destroy", forget_ptree_widget, NULL);
1671 }
1672
1673 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1674 static void
1675 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1676 {
1677   ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1678 }
1679
1680 /* Set the selection mode of a given packet tree window. */
1681 static void
1682 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1683 {
1684 #if GTK_MAJOR_VERSION >= 2
1685     GtkTreeSelection *selection;
1686
1687     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1688 #endif
1689     /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1690        I think "browse" in Wireshark makes more sense than "SINGLE" in
1691        GTK+ */
1692     if (val) {
1693 #if GTK_MAJOR_VERSION < 2
1694         gtk_clist_set_selection_mode(GTK_CLIST(tree),
1695                                      GTK_SELECTION_SINGLE);
1696 #else
1697         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1698 #endif
1699     }
1700     else {
1701 #if GTK_MAJOR_VERSION < 2
1702         gtk_clist_set_selection_mode(GTK_CLIST(tree),
1703                                      GTK_SELECTION_BROWSE);
1704 #else
1705         gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1706 #endif
1707     }
1708 }
1709
1710 static void
1711 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1712 {
1713         set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1714 }
1715
1716 /* Set the selection mode of all packet tree windows. */
1717 void
1718 set_ptree_sel_browse_all(gboolean val)
1719 {
1720         g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1721 }
1722
1723 #if GTK_MAJOR_VERSION < 2
1724 static void
1725 set_ptree_style_cb(gpointer data, gpointer user_data)
1726 {
1727         gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
1728 }
1729 #else
1730 static void
1731 set_ptree_font_cb(gpointer data, gpointer user_data)
1732 {
1733         gtk_widget_modify_font((GtkWidget *)data,
1734                                (PangoFontDescription *)user_data);
1735 }
1736 #endif
1737
1738 void
1739 set_ptree_font_all(FONT_TYPE *font)
1740 {
1741 #if GTK_MAJOR_VERSION < 2
1742     GtkStyle *style;
1743
1744     style = gtk_style_new();
1745     gdk_font_unref(style->font);
1746     style->font = font;
1747     gdk_font_ref(font);
1748
1749     g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
1750
1751     /* Now nuke the old style and replace it with the new one. */
1752     gtk_style_unref(item_style);
1753     item_style = style;
1754 #else
1755     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1756 #endif
1757 }
1758
1759
1760 gboolean colors_ok = FALSE;
1761 GdkColor        expert_color_chat       = { 0, 0xcc00, 0xcc00, 0xe000 };        /* a pale bluegrey */
1762 GdkColor        expert_color_note       = { 0, 0xa000, 0xff00, 0xff00 };        /* a bright turquoise */
1763 GdkColor        expert_color_warn       = { 0, 0xff00, 0xff00, 0 };                     /* yellow */
1764 GdkColor        expert_color_error      = { 0, 0xff00, 0x5c00, 0x5c00 };        /* pale red */
1765
1766 void proto_draw_colors_init(void)
1767 {
1768         if(colors_ok) {
1769                 return;
1770         }
1771
1772         get_color(&expert_color_chat);
1773         get_color(&expert_color_note);
1774         get_color(&expert_color_warn);
1775         get_color(&expert_color_error);
1776
1777         colors_ok = TRUE;
1778 }
1779
1780
1781 #if GTK_MAJOR_VERSION >= 2
1782 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1783                                              GtkCellRenderer *cell,
1784                                              GtkTreeModel *tree_model,
1785                                              GtkTreeIter *iter,
1786                                              gpointer data _U_)
1787 {
1788     field_info   *fi;
1789
1790     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1791
1792         if(!colors_ok) {
1793                 proto_draw_colors_init();
1794         }
1795
1796         /* for the various possible attributes, see:
1797          * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1798          *
1799          * color definitions can be found at:
1800          * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1801          * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1802          *
1803          * some experiences:
1804          * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1805          * weight/style: doesn't take any effect
1806          */
1807
1808     /* for each field, we have to reset the renderer attributes */
1809     g_object_set (cell, "foreground-set", FALSE, NULL);
1810
1811     g_object_set (cell, "background", "white", NULL);
1812     g_object_set (cell, "background-set", TRUE, NULL);
1813
1814     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1815     g_object_set (cell, "underline-set", FALSE, NULL);
1816
1817     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1818     g_object_set (cell, "style-set", FALSE, NULL);*/
1819
1820     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1821     g_object_set (cell, "weight-set", FALSE, NULL);*/
1822
1823     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1824                 /* we use "[...]" to mark generated items, no need to change things here */
1825
1826         /* as some fonts don't support italic, don't use this */
1827         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1828         g_object_set (cell, "style-set", TRUE, NULL);
1829         */
1830         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1831         g_object_set (cell, "weight-set", TRUE, NULL);*/
1832     }
1833
1834     if(fi->hfinfo->type == FT_PROTOCOL) {
1835         g_object_set (cell, "background", "gray90", NULL);
1836         g_object_set (cell, "background-set", TRUE, NULL);
1837         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1838         g_object_set (cell, "weight-set", TRUE, NULL);*/
1839         }
1840
1841     if((fi->hfinfo->type == FT_FRAMENUM) ||
1842        (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1843         g_object_set (cell, "foreground", "blue", NULL);
1844         g_object_set (cell, "foreground-set", TRUE, NULL);
1845
1846         g_object_set (cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1847         g_object_set (cell, "underline-set", TRUE, NULL);
1848     }
1849
1850         if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1851                 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1852                 case(PI_CHAT):
1853                         g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1854                         g_object_set (cell, "background-set", TRUE, NULL);
1855                         break;
1856                 case(PI_NOTE):
1857                         g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1858                         g_object_set (cell, "background-set", TRUE, NULL);
1859                         break;
1860                 case(PI_WARN):
1861                         g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1862                         g_object_set (cell, "background-set", TRUE, NULL);
1863                         break;
1864                 case(PI_ERROR):
1865                         g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1866                         g_object_set (cell, "background-set", TRUE, NULL);
1867                         break;
1868                 default:
1869                         g_assert_not_reached();
1870                 }
1871         }
1872 }
1873 #endif
1874
1875 GtkWidget *
1876 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1877 {
1878   GtkWidget *tv_scrollw, *tree_view;
1879 #if GTK_MAJOR_VERSION >= 2
1880   GtkTreeStore *store;
1881   GtkCellRenderer *renderer;
1882   GtkTreeViewColumn *column;
1883   gint col_offset;
1884 #endif
1885
1886   /* Tree view */
1887   tv_scrollw = scrolled_window_new(NULL, NULL);
1888 #if GTK_MAJOR_VERSION >= 2
1889   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1890                                    GTK_SHADOW_IN);
1891 #endif
1892
1893 #if GTK_MAJOR_VERSION < 2
1894   tree_view = ctree_new(1, 0);
1895   SIGNAL_CONNECT(tree_view, "key-press-event", toggle_tree, NULL );
1896   SIGNAL_CONNECT(tree_view, "tree-expand", expand_tree, NULL );
1897   SIGNAL_CONNECT(tree_view, "tree-collapse", collapse_tree, NULL );
1898   /* I need this next line to make the widget work correctly with hidden
1899    * column titles and GTK_SELECTION_BROWSE */
1900   gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1901 #else
1902   store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1903   tree_view = tree_view_new(GTK_TREE_MODEL(store));
1904   g_object_unref(G_OBJECT(store));
1905   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1906   renderer = gtk_cell_renderer_text_new();
1907   g_object_set (renderer, "ypad", 0, NULL);
1908   col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1909                                                            -1, "Name", renderer,
1910                                                            "text", 0, NULL);
1911   column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1912                                     col_offset - 1);
1913   gtk_tree_view_column_set_cell_data_func(column,
1914                                              renderer,
1915                                              tree_cell_renderer,
1916                                              NULL,
1917                                              NULL);
1918
1919   gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1920                                   GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1921   SIGNAL_CONNECT(tree_view, "row-expanded", expand_tree, NULL);
1922   SIGNAL_CONNECT(tree_view, "row-collapsed", collapse_tree, NULL);
1923 #endif
1924   gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1925   set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1926 #if GTK_MAJOR_VERSION < 2
1927   if(item_style == NULL) {
1928       item_style = gtk_style_new();
1929       gdk_font_unref(item_style->font);
1930       item_style->font = user_font_get_regular();
1931   }
1932
1933   gtk_widget_set_style(tree_view, item_style);
1934 #else
1935   gtk_widget_modify_font(tree_view, user_font_get_regular());
1936 #endif
1937   remember_ptree_widget(tree_view);
1938
1939   *tree_view_p = tree_view;
1940
1941   return tv_scrollw;
1942 }
1943
1944 #if GTK_MAJOR_VERSION < 2
1945 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1946 #else
1947 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1948 #endif
1949 {
1950   int i;
1951   for(i=0; i < num_tree_types; i++) {
1952     tree_is_expanded[i] = TRUE;
1953   }
1954 #if GTK_MAJOR_VERSION < 2
1955   proto_tree_draw(protocol_tree, tree_view);
1956   gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
1957 #else
1958   gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1959 #endif
1960 }
1961
1962 #if GTK_MAJOR_VERSION < 2
1963 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1964 #else
1965 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1966 #endif
1967 {
1968   int i;
1969   for(i=0; i < num_tree_types; i++) {
1970     tree_is_expanded[i] = FALSE;
1971   }
1972 #if GTK_MAJOR_VERSION < 2
1973   proto_tree_draw(protocol_tree, tree_view);
1974 #else
1975   gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1976 #endif
1977 }
1978
1979
1980 struct proto_tree_draw_info {
1981 #if GTK_MAJOR_VERSION < 2
1982     GtkCTree     *ctree;
1983     GtkCTreeNode *ctree_node;
1984 #else
1985     GtkTreeView  *tree_view;
1986     GtkTreeIter  *iter;
1987 #endif
1988 };
1989
1990 void
1991 main_proto_tree_draw(proto_tree *protocol_tree)
1992 {
1993     proto_tree_draw(protocol_tree, tree_view);
1994 }
1995
1996
1997 static void
1998 tree_view_follow_link(field_info   *fi)
1999 {
2000     gchar *url;
2001
2002     if(fi->hfinfo->type == FT_FRAMENUM) {
2003         cf_goto_frame(&cfile, fi->value.value.uinteger);
2004     }
2005     if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
2006             url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
2007       browser_open_url(url);
2008       g_free(url);
2009     }
2010 }
2011
2012
2013 /* If the user selected a position in the tree view, try to find
2014  * the item in the GUI proto_tree that corresponds to that byte, and
2015  * select it. */
2016 gboolean
2017 tree_view_select(GtkWidget *widget, GdkEventButton *event)
2018 {
2019 #if GTK_MAJOR_VERSION < 2
2020         GtkCTree     *ctree;
2021         GtkCTreeNode *node;
2022         gint         row;
2023         gint         column;
2024         field_info   *fi;
2025
2026
2027         if(gtk_clist_get_selection_info(GTK_CLIST(widget),
2028             (gint) (((GdkEventButton *)event)->x),
2029             (gint) (((GdkEventButton *)event)->y),
2030             &row, &column))
2031         {
2032             ctree = GTK_CTREE(widget);
2033
2034             node = gtk_ctree_node_nth(ctree, row);
2035             g_assert(node);
2036
2037             gtk_ctree_select(ctree, node);
2038
2039             /* if that's a doubleclick, try to follow the link */
2040             if(event->type == GDK_2BUTTON_PRESS) {
2041                 fi = gtk_ctree_node_get_row_data(ctree, node);
2042                 tree_view_follow_link(fi);
2043             }
2044         } else {
2045             return FALSE;
2046         }
2047 #else
2048         GtkTreeSelection    *sel;
2049         GtkTreePath         *path;
2050
2051         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
2052                                           (gint) (((GdkEventButton *)event)->x),
2053                                           (gint) (((GdkEventButton *)event)->y),
2054                                           &path, NULL, NULL, NULL))
2055         {
2056             sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2057             gtk_tree_selection_select_path(sel, path);
2058
2059             /* if that's a doubleclick, try to follow the link */
2060             if(event->type == GDK_2BUTTON_PRESS) {
2061                 GtkTreeModel *model;
2062                 GtkTreeIter iter;
2063                 field_info   *fi;
2064
2065                 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
2066                     gtk_tree_model_get(model, &iter, 1, &fi, -1);
2067                     tree_view_follow_link(fi);
2068                 }
2069             }
2070         } else {
2071             return FALSE;
2072         }
2073 #endif
2074     return TRUE;
2075 }
2076
2077 /* fill the whole protocol tree with the string values */
2078 void
2079 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
2080 {
2081 #if GTK_MAJOR_VERSION >= 2
2082     GtkTreeStore *store;
2083 #endif
2084     struct proto_tree_draw_info info;
2085
2086 #if GTK_MAJOR_VERSION < 2
2087     info.ctree = GTK_CTREE(tree_view);
2088     info.ctree_node = NULL;
2089
2090     gtk_clist_freeze(GTK_CLIST(tree_view));
2091 #else
2092     info.tree_view = GTK_TREE_VIEW(tree_view);
2093     info.iter = NULL;
2094
2095     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
2096 #endif
2097
2098     /*
2099      * Clear out any crud left over in the display of the protocol
2100      * tree, by removing all nodes from the tree.
2101      * This is how it's done in testgtk.c in GTK+.
2102      */
2103 #if GTK_MAJOR_VERSION < 2
2104     gtk_clist_clear(GTK_CLIST(tree_view));
2105 #else
2106     gtk_tree_store_clear(store);
2107 #endif
2108
2109     proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2110
2111 #if GTK_MAJOR_VERSION < 2
2112     gtk_clist_thaw(GTK_CLIST(tree_view));
2113 #endif
2114 }
2115
2116
2117 /* fill a single protocol tree item with the string value */
2118 static void
2119 proto_tree_draw_node(proto_node *node, gpointer data)
2120 {
2121     struct proto_tree_draw_info info;
2122     struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2123
2124     field_info   *fi = PITEM_FINFO(node);
2125     gchar         label_str[ITEM_LABEL_LENGTH];
2126     gchar        *label_ptr;
2127     gboolean      is_leaf, is_expanded;
2128 #if GTK_MAJOR_VERSION < 2
2129     GtkCTreeNode *parent;
2130 #else
2131     GtkTreeStore *store;
2132     GtkTreeIter   iter;
2133     GtkTreePath  *path;
2134 #endif
2135
2136     if (PROTO_ITEM_IS_HIDDEN(node))
2137         return;
2138
2139     /* was a free format label produced? */
2140     if (fi->rep) {
2141         label_ptr = fi->rep->representation;
2142     }
2143     else { /* no, make a generic label */
2144         label_ptr = label_str;
2145         proto_item_fill_label(fi, label_str);
2146     }
2147
2148     if (node->first_child != NULL) {
2149         is_leaf = FALSE;
2150         g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2151         if (tree_is_expanded[fi->tree_type]) {
2152             is_expanded = TRUE;
2153         }
2154         else {
2155             is_expanded = FALSE;
2156         }
2157     }
2158     else {
2159         is_leaf = TRUE;
2160         is_expanded = FALSE;
2161     }
2162
2163     if(PROTO_ITEM_IS_GENERATED(node)) {
2164         label_ptr = g_strdup_printf("[%s]", label_ptr);
2165     }
2166
2167 #if GTK_MAJOR_VERSION < 2
2168     info.ctree = parent_info->ctree;
2169     parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
2170                                      &label_ptr, 5, NULL, NULL, NULL, NULL,
2171                                      is_leaf, is_expanded );
2172
2173     gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
2174 #else
2175     info.tree_view = parent_info->tree_view;
2176     store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2177     gtk_tree_store_append(store, &iter, parent_info->iter);
2178     gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2179 #endif
2180
2181     if(PROTO_ITEM_IS_GENERATED(node)) {
2182         g_free(label_ptr);
2183     }
2184
2185     if (!is_leaf) {
2186 #if GTK_MAJOR_VERSION < 2
2187         info.ctree_node = parent;
2188 #else
2189         info.iter = &iter;
2190 #endif
2191         proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2192 #if GTK_MAJOR_VERSION >= 2
2193         path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2194         if (is_expanded)
2195 /* #if GTK_MINOR_VERSION >= 2 */
2196             gtk_tree_view_expand_to_path(info.tree_view, path);
2197 /*#else
2198             gtk_tree_view_expand_row(info.tree_view, path, FALSE);
2199 #endif*/
2200         else
2201             gtk_tree_view_collapse_row(info.tree_view, path);
2202         gtk_tree_path_free(path);
2203 #endif
2204     }
2205 }
2206
2207 /*
2208  * Clear the hex dump and protocol tree panes.
2209  */
2210 void
2211 clear_tree_and_hex_views(void)
2212 {
2213   /* Clear the hex dump by getting rid of all the byte views. */
2214   while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
2215     gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
2216
2217   /* Add a placeholder byte view so that there's at least something
2218      displayed in the byte view notebook. */
2219   add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
2220
2221   /* Clear the protocol tree by removing all nodes in the ctree.
2222      This is how it's done in testgtk.c in GTK+ */
2223 #if GTK_MAJOR_VERSION < 2
2224   gtk_clist_clear(GTK_CLIST(tree_view));
2225 #else
2226   gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));
2227 #endif
2228 }
2229