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