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