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