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