2 * Routines for GTK+ packet display
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * Jeff Foster, 2001/03/12, added support for displaying named
11 * data sources as tabbed hex windows
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.
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.
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.
43 #include <gdk/gdkkeysyms.h>
44 #if GTK_CHECK_VERSION(3,0,0)
45 # include <gdk/gdkkeysyms-compat.h>
50 #include <epan/epan_dissect.h>
52 #include <epan/packet.h>
53 #include <epan/charsets.h>
54 #include <epan/prefs.h>
55 #include <epan/filesystem.h>
57 #include "../isprint.h"
58 #include "../alert_box.h"
59 #include "../simple_dialog.h"
60 #include "../progress_dlg.h"
61 #include "../ui_util.h"
62 #include <wsutil/file_util.h>
65 #include "gtk/color_utils.h"
66 #include "gtk/capture_file_dlg.h"
67 #include "gtk/packet_win.h"
68 #include "gtk/file_dlg.h"
69 #include "gtk/gui_utils.h"
70 #include "gtk/gtkglobals.h"
71 #include "gtk/font_utils.h"
72 #include "gtk/webbrowser.h"
74 #include "gtk/menus.h"
75 #include "gtk/main_proto_draw.h"
76 #include "gtk/recent.h"
79 #include <gdk/gdkwin32.h>
81 #include "win32/file_dlg_win32.h"
85 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
86 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
87 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
88 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
89 #define E_BYTE_VIEW_START_KEY "byte_view_start"
90 #define E_BYTE_VIEW_END_KEY "byte_view_end"
91 #define E_BYTE_VIEW_MASK_KEY "byte_view_mask"
92 #define E_BYTE_VIEW_MASKLE_KEY "byte_view_mask_le"
93 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
94 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
95 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
98 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
99 proto_tree *tree, GtkWidget *tree_view);
102 proto_tree_draw_node(proto_node *node, gpointer data);
104 /* Get the current text window for the notebook. */
106 get_notebook_bv_ptr(GtkWidget *nb_ptr)
111 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
112 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
114 return gtk_bin_get_child(GTK_BIN(bv_page));
120 * Get the data and length for a byte view, given the byte view page.
121 * Return the pointer, or NULL on error, and set "*data_len" to the length.
124 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
126 tvbuff_t *byte_view_tvb;
127 const guint8 *data_ptr;
129 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
130 if (byte_view_tvb == NULL)
133 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
134 *data_len = tvb_length(byte_view_tvb);
139 * Set the current text window for the notebook to the window that
140 * refers to a particular tvbuff.
143 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
146 GtkWidget *bv_page, *bv;
150 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
152 bv = gtk_bin_get_child(GTK_BIN(bv_page));
153 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
156 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
162 /* Redraw a given byte view window. */
164 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
170 bv = get_notebook_bv_ptr(nb);
172 data = get_byte_view_data_and_length(bv, &len);
174 packet_hex_print(bv, data, fd, finfo, len);
178 /* Redraw all byte view windows. */
180 redraw_packet_bytes_all(void)
182 if (cfile.current_frame != NULL)
183 redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
185 redraw_packet_bytes_packet_wins();
187 /* XXX - this is a hack, to workaround a bug in GTK2.x!
188 when changing the font size, even refilling of the corresponding
189 gtk_text_buffer doesn't seem to trigger an update.
190 The only workaround is to freshly select the frame, which will remove any
191 existing notebook tabs and "restart" the whole byte view again. */
192 if (cfile.current_frame != NULL) {
193 cfile.current_row = -1;
194 cf_goto_frame(&cfile, cfile.current_frame->num);
199 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
200 GtkTreePath *path _U_, gpointer user_data _U_)
205 model = gtk_tree_view_get_model(tree_view);
206 gtk_tree_model_get(model, iter, 1, &finfo, -1);
209 /* scroll the expanded item to reduce the need to do a manual scroll down
210 * and provide faster navigation of deeper trees */
212 if(prefs.gui_auto_scroll_on_expand)
213 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
216 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
217 * are thus presumably leaf nodes and cannot be expanded.
219 if (finfo->tree_type != -1) {
220 g_assert(finfo->tree_type >= 0 &&
221 finfo->tree_type < num_tree_types);
222 tree_is_expanded[finfo->tree_type] = TRUE;
227 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
228 GtkTreePath *path _U_, gpointer user_data _U_)
233 model = gtk_tree_view_get_model(tree_view);
234 gtk_tree_model_get(model, iter, 1, &finfo, -1);
238 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
239 * are thus presumably leaf nodes and cannot be collapsed.
241 if (finfo->tree_type != -1) {
242 g_assert(finfo->tree_type >= 0 &&
243 finfo->tree_type < num_tree_types);
244 tree_is_expanded[finfo->tree_type] = FALSE;
248 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
249 #define BYTES_PER_LINE 16 /* max byte values in a line */
250 #define BITS_PER_LINE 8 /* max bit values in a line */
251 #define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
252 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
253 /* max number of characters hex dump takes -
254 2 digits plus trailing blank
255 plus separator between first and
257 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
258 /* number of characters those bytes take;
259 3 characters per byte of hex dump,
260 2 blanks separating hex from ASCII,
261 1 character per byte of ASCII dump */
262 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
263 /* number of characters per line;
264 offset, 2 blanks separating offset
265 from data dump, data dump */
266 #define MAX_LINES 100
267 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
269 /* Which byte the offset is referring to. Associates
270 * whitespace with the preceding digits. */
272 byte_num(int offset, int start_point)
274 return (offset - start_point) / 3;
277 bit_num(int offset, int start_point)
279 return (offset - start_point) / 9;
282 struct field_lookup_info {
288 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
292 struct field_lookup_info *fli = (struct field_lookup_info *)data;
294 gtk_tree_model_get(model, iter, 1, &fi, -1);
303 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
306 struct field_lookup_info fli;
308 g_assert(finfo != NULL);
310 model = gtk_tree_view_get_model(tree_view);
312 gtk_tree_model_foreach(model, lookup_finfo, &fli);
314 return gtk_tree_model_get_path(model, &fli.iter);
318 hex_view_get_byte(guint ndigits, int row, int column)
331 * The column of the first hex digit in the first half.
332 * That starts after "ndigits" digits of offset and two
335 digits_start_1 = ndigits + 2;
338 * The column of the last hex digit in the first half.
339 * There are BYTES_PER_LINE/2 bytes displayed in the first
340 * half; there are 2 characters per byte, plus a separating
341 * blank after all but the last byte's characters.
343 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
344 (BYTES_PER_LINE/2 - 1);
347 * The column of the first hex digit in the second half.
348 * Add 2 for the 2 separating blanks between the halves.
350 digits_start_2 = digits_end_1 + 2;
353 * The column of the last hex digit in the second half.
354 * Add the same value we used to get "digits_end_1" from
357 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
358 (BYTES_PER_LINE/2 - 1);
361 * The column of the first "text dump" character in the first half.
362 * Add 3 for the 3 separating blanks between the hex and text dump.
364 text_start_1 = digits_end_2 + 3;
367 * The column of the last "text dump" character in the first half.
368 * There are BYTES_PER_LINE/2 bytes displayed in the first
369 * half; there is 1 character per byte.
371 * Then subtract 1 to get the last column of the first half
372 * rather than the first column after the first half.
374 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
377 * The column of the first "text dump" character in the second half.
378 * Add back the 1 to get the first column after the first half,
379 * and then add 1 for the separating blank between the halves.
381 text_start_2 = text_end_1 + 2;
384 * The column of the last "text dump" character in second half.
385 * Add the same value we used to get "text_end_1" from
388 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
390 /* Given the column and row, determine which byte offset
391 * the user clicked on. */
392 if (column >= digits_start_1 && column <= digits_end_1) {
393 byte = byte_num(column, digits_start_1);
398 else if (column >= digits_start_2 && column <= digits_end_2) {
399 byte = byte_num(column, digits_start_2);
405 else if (column >= text_start_1 && column <= text_end_1) {
406 byte = column - text_start_1;
408 else if (column >= text_start_2 && column <= text_end_2) {
409 byte = 8 + column - text_start_2;
412 /* The user didn't select a hex digit or
413 * text-dump character. */
417 /* Add the number of bytes from the previous rows. */
418 byte += row * BYTES_PER_LINE;
424 bit_view_get_byte(guint ndigits, int row, int column)
433 * The column of the first bit digit.
434 * That starts after "ndigits" digits of offset and two
437 digits_start = ndigits + 2;
440 * The column of the last bit digit.
441 * There are BITS_PER_LINE bytes displayed; there are
442 * 8 characters per byte, plus a separating blank
443 * after all but the last byte's characters.
445 digits_end = digits_start + (BITS_PER_LINE)*8 +
449 * The column of the first "text dump" character.
450 * Add 3 for the 3 separating blanks between the bit and text dump.
452 text_start = digits_end + 3;
455 * The column of the last "text dump" character.
456 * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
458 * Then subtract 1 to get the last column.
460 text_end = text_start + BITS_PER_LINE - 1;
462 /* Given the column and row, determine which byte offset
463 * the user clicked on. */
464 if (column >= digits_start && column <= digits_end) {
465 byte = bit_num(column, digits_start);
470 else if (column >= text_start && column <= text_end) {
471 byte = column - text_start;
474 /* The user didn't select a hex digit or
475 * text-dump character. */
479 /* Add the number of bytes from the previous rows. */
480 byte += row * BITS_PER_LINE;
485 /* If the user selected a certain byte in the byte view, try to find
486 * the item in the GUI proto_tree that corresponds to that byte, and:
488 * if we succeed, select it, and return TRUE;
489 * if we fail, return FALSE. */
491 byte_view_select(GtkWidget *widget, GdkEventButton *event)
493 GtkTextView *bv = GTK_TEXT_VIEW(widget);
495 GtkTreeView *tree_view;
503 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
506 * Somebody clicked on the dummy byte view; do nothing.
510 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
511 E_BYTE_VIEW_TREE_VIEW_PTR));
513 /* get the row/column selected */
514 gtk_text_view_window_to_buffer_coords(bv,
515 gtk_text_view_get_window_type(bv, event->window),
516 (gint) event->x, (gint) event->y, &x, &y);
517 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
518 row = gtk_text_iter_get_line(&iter);
519 column = gtk_text_iter_get_line_offset(&iter);
522 * Get the number of digits of offset being displayed, and
523 * compute the byte position in the buffer.
525 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
527 switch (recent.gui_bytes_view) {
529 byte = hex_view_get_byte(ndigits, row, column);
532 byte = bit_view_get_byte(ndigits, row, column);
535 g_assert_not_reached();
542 /* Get the data source tvbuff */
543 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
545 return highlight_field(tvb, byte, tree_view, tree);
548 /* This highlights the field in the proto tree that is at position byte */
550 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
553 GtkTreeModel *model = NULL;
554 GtkTreePath *first_path = NULL, *path = NULL;
556 field_info *finfo = NULL;
558 struct field_lookup_info fli;
560 if (cfile.search_in_progress && cfile.string && cfile.decode_data) {
561 /* The tree where the target string matched one of the labels was discarded in
562 match_protocol_tree() so we have to search again in the latest tree. (Uugh) */
563 if (cf_find_string_protocol_tree(&cfile, tree, &mdata)) {
567 /* Find the finfo that corresponds to our byte. */
568 finfo = proto_find_field_from_offset(tree, byte, tvb);
575 model = gtk_tree_view_get_model(tree_view);
577 gtk_tree_model_foreach(model, lookup_finfo, &fli);
579 /* Expand our field's row */
580 first_path = gtk_tree_model_get_path(model, &fli.iter);
581 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
582 expand_tree(tree_view, &fli.iter, NULL, NULL);
584 /* ... and its parents */
585 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
586 path = gtk_tree_model_get_path(model, &parent);
587 gtk_tree_view_expand_row(tree_view, path, FALSE);
588 expand_tree(tree_view, &parent, NULL, NULL);
590 gtk_tree_path_free(path);
593 /* Refresh the display so that the expanded trees are visible */
594 main_proto_tree_draw(tree);
596 /* select our field's row */
597 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
600 /* If the last search was a string or hex search within "Packet data", the entire field might
601 not be highlighted. If the user just clicked on one of the bytes comprising that field, the
602 above call didn't trigger a 'gtk_tree_view_get_selection' event. Call redraw_packet_bytes()
603 to make the highlighting of the entire field visible. */
604 if (!cfile.search_in_progress) {
605 if (cfile.hex || (cfile.string && cfile.packet_data)) {
606 redraw_packet_bytes(byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
610 /* And position the window so the selection is visible.
611 * Position the selection in the middle of the viewable
613 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
615 gtk_tree_path_free(first_path);
620 /* Calls functions for different mouse-button presses. */
622 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
624 GdkEventButton *event_button = NULL;
626 if(widget == NULL || event == NULL || data == NULL) {
630 if(event->type == GDK_BUTTON_PRESS) {
631 event_button = (GdkEventButton *) event;
633 /* To qoute the "Gdk Event Structures" doc:
634 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
635 switch(event_button->button) {
638 return byte_view_select(widget, event_button);
640 return popup_menu_handler(widget, event, data);
654 byte_nb = gtk_notebook_new();
655 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
657 /* this will only have an effect, if no tabs are shown */
658 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
660 /* set the tabs scrollable, if they don't fit into the pane */
661 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
663 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
664 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
666 /* Add a placeholder byte view so that there's at least something
667 displayed in the byte view notebook. */
668 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
674 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
676 const guint8 *byte_data;
679 byte_data = get_byte_view_data_and_length(bv, &byte_len);
680 if (byte_data == NULL) {
681 /* This must be the dummy byte view if no packet is selected. */
684 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
688 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
689 proto_tree *tree, GtkWidget *tree_view)
691 GtkWidget *byte_view, *byte_scrollw, *label;
693 #if GTK_CHECK_VERSION(3,0,0)
694 GtkStyleContext *context;
695 GdkRGBA *rgba_bg_color;
696 GdkRGBA *rgba_fg_color;
701 /* Byte view. Create a scrolled window for the text. */
702 byte_scrollw = scrolled_window_new(NULL, NULL);
703 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
705 /* Add scrolled pane to tabbed window */
706 label = gtk_label_new(name);
707 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
709 gtk_widget_show(byte_scrollw);
711 byte_view = gtk_text_view_new();
712 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
713 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
714 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
715 #if GTK_CHECK_VERSION(3,0,0)
716 context = gtk_widget_get_style_context (GTK_WIDGET(top_level));
717 gtk_style_context_get (context, GTK_STATE_SELECTED,
718 "background-color", &rgba_bg_color,
720 gtk_style_context_get (context, GTK_STATE_SELECTED,
721 "color", &rgba_fg_color,
724 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
725 gtk_text_buffer_create_tag(buf, "reverse",
726 "font-desc", user_font_get_regular(),
727 "foreground-rgba", &rgba_fg_color,
728 "background-rgba", &rgba_bg_color,
732 style = gtk_widget_get_style(GTK_WIDGET(top_level));
733 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
734 gtk_text_buffer_create_tag(buf, "reverse",
735 "font-desc", user_font_get_regular(),
736 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
737 "background-gdk", &style->base[GTK_STATE_SELECTED],
740 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
741 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
742 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
744 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
745 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
746 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
748 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
749 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
751 gtk_widget_show(byte_view); /* triggers byte_view_realize_cb which calls packet_hex_print */
753 /* no tabs if this is the first page */
754 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
755 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
757 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
760 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
761 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
767 add_main_byte_views(epan_dissect_t *edt)
769 add_byte_views(edt, tree_view_gbl, byte_nb_ptr_gbl);
773 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
774 GtkWidget *byte_nb_ptr)
780 * Get rid of all the old notebook tabs.
782 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
783 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
786 * Add to the specified byte view notebook tabs for hex dumps
787 * of all the data sources for the specified frame.
789 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
791 add_byte_tab(byte_nb_ptr, get_data_source_name(src), src->tvb, edt->tree,
796 * Initially select the first byte view.
798 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
803 static GtkWidget *savehex_dlg=NULL;
806 savehex_dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
813 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
815 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
817 gboolean end_of_line = TRUE; /* Initial state is end of line */
818 int byte_line_part_length;
823 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
824 hex_str = g_string_new("");
825 char_str= g_string_new("");
830 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
833 g_string_append_printf(hex_str," %02x",*data_p);
835 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
840 /* Look ahead to see if this is the end of the data */
841 byte_line_part_length = (++i) % byte_line_length;
843 /* End of data - need to fill in spaces in hex string and then do "end of line".
846 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
847 g_string_append(hex_str," "); /* Three spaces for each missing byte */
851 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
857 g_string_append(copy_buffer, hex_str->str);
859 /* Two spaces between hex and text */
860 g_string_append_c(copy_buffer, ' ');
861 g_string_append_c(copy_buffer, ' ');
862 g_string_append(copy_buffer, char_str->str);
864 /* Setup ready for next line */
865 g_string_assign(char_str,"");
866 g_string_assign(hex_str, "\n");
870 g_string_free(hex_str, TRUE);
871 g_string_free(char_str, TRUE);
875 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
880 /* Copy printable characters, newlines, and (horizontal) tabs. */
881 if(isprint(*data_p)) {
883 } else if(*data_p==0x0a) {
885 } else if(*data_p==0x09) {
888 return 1; /* Just ignore non-printable bytes */
890 g_string_append_c(copy_buffer,to_append);
895 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
897 g_string_append_printf(copy_buffer, "%02x", *data_p);
902 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
907 int bytes_consumed = 0;
910 const guint8* data_p;
912 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
914 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
916 /* shouldn't happen */
917 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
921 data_p = get_byte_view_data_and_length(bv, &len);
922 g_assert(data_p != NULL);
924 flags = data_type & CD_FLAGSMASK;
925 data_type = data_type & CD_TYPEMASK;
927 if(flags & CD_FLAGS_SELECTEDONLY) {
930 /* Get the start and end of the highlighted bytes. */
931 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
932 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
934 if(start >= 0 && end > start && (end - start <= (int)len)) {
942 /* This is too different from other text formats - handle separately */
943 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
946 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
947 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
950 /* Completely different logic to text copies - leave copy buffer alone */
951 copy_binary_to_clipboard(data_p,len);
954 /* Incrementally write to text buffer in various formats */
958 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
961 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
964 g_assert_not_reached();
968 g_assert(bytes_consumed>0);
969 data_p += bytes_consumed;
970 len -= bytes_consumed;
975 if(copy_buffer->len > 0) {
976 copy_to_clipboard(copy_buffer);
979 g_string_free(copy_buffer, TRUE);
982 /* save the current highlighted hex data */
984 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
989 const guint8 *data_p = NULL;
992 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
994 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
995 if (!file ||! *file) {
996 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
1001 if (test_for_directory(file) == EISDIR) {
1002 /* It's a directory - set the file selection box to display that
1003 directory, and leave the selection box displayed. */
1004 set_last_open_dir(file);
1006 file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
1007 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
1008 return FALSE; /* do gtk_dialog_run again */
1011 /* XXX: Must check if file name exists first */
1013 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
1015 /* shouldn't happen */
1016 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1021 * Retrieve the info we need
1023 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1024 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1025 data_p = get_byte_view_data_and_length(bv, &len);
1027 if (data_p == NULL || start == -1 || start > end) {
1028 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1029 "No data selected to save!");
1034 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
1036 open_failure_alert_box(file, errno, TRUE);
1040 if (ws_write(fd, data_p + start, end - start) < 0) {
1041 write_failure_alert_box(file, errno);
1046 if (ws_close(fd) < 0) {
1047 write_failure_alert_box(file, errno);
1052 /* Get rid of the dialog box */
1054 #if 0 /* being handled by caller (for now) */
1055 window_destroy(GTK_WIDGET(savehex_dlg));
1060 /* Launch the dialog box to put up the file selection box etc */
1063 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1065 win32_export_raw_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
1070 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1074 const guint8 *data_p = NULL;
1079 /* don't show up the dialog, if no data has to be saved */
1080 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
1082 /* shouldn't happen */
1083 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1086 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1087 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1088 data_p = get_byte_view_data_and_length(bv, &len);
1090 if (data_p == NULL || start == -1 || start > end) {
1091 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1095 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
1096 /* if the window is already open, bring it to front */
1098 reactivate_window(savehex_dlg);
1103 * Build the dialog box we need.
1105 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1106 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
1109 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1110 end - start, plurality(end - start, "byte", "bytes"));
1111 dlg_lb = gtk_label_new(label);
1113 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1114 gtk_widget_show(dlg_lb);
1116 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
1119 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1120 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1122 window_destroy(savehex_dlg);
1125 /* "Run" the GtkFileChooserDialog. */
1126 /* Upon exit: If "Accept" run the OK callback. */
1127 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
1128 /* If not accept (ie: cancel) destroy the window. */
1129 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
1130 /* return with a TRUE status so that the dialog window will be destroyed. */
1131 /* Trying to re-run the dialog after popping up an alert box will not work */
1132 /* since the user will not be able to dismiss the alert box. */
1133 /* The (somewhat unfriendly) effect: the user must re-invoke the */
1134 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
1136 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
1137 /* GtkFileChooserDialog. */
1138 while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1139 if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
1140 break; /* we're done */
1143 window_destroy(savehex_dlg);
1147 static GtkTextMark *
1148 packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int bstart, int bend, guint32 mask, int mask_le, int use_digits, int create_mark)
1150 GtkTextIter i_start, i_stop, iter;
1152 GtkTextTag *revstyle_tag;
1153 const char *revstyle;
1157 int bits_per_one = 0;
1158 int hex_offset, ascii_offset;
1160 int start_line, start_line_pos;
1161 int stop_line, stop_line_pos;
1163 if (bstart == -1 || bend == -1)
1166 /* Display with inverse video ? */
1167 if (prefs.gui_hex_dump_highlight_style)
1168 revstyle = "reverse";
1172 revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle);
1174 switch (recent.gui_bytes_view) {
1176 per_line = BYTES_PER_LINE;
1177 per_one = 2+1; /* "ff " */
1181 per_line = BITS_PER_LINE;
1182 per_one = 8+1; /* "10101010 " */
1186 g_assert_not_reached();
1189 start_line = bstart / per_line;
1190 start_line_pos = bstart % per_line;
1192 stop_line = bend / per_line;
1193 stop_line_pos = bend % per_line;
1195 #define hex_fix(pos) hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1196 #define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1198 hex_offset = use_digits + 2;
1199 ascii_offset = hex_fix(per_line) + 2;
1201 gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos));
1204 while (start_line <= stop_line) {
1205 int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1206 int first_block_adjust = (recent.gui_bytes_view == BYTES_HEX) ? (line_pos_end == per_line/2) : 0;
1208 if (start_line_pos == line_pos_end) break;
1211 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
1212 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1-first_block_adjust);
1213 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1216 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
1217 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end)-first_block_adjust);
1218 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1224 } else if (mask_le) { /* LSB of mask first (little-endian) */
1225 while (start_line <= stop_line) {
1226 int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1227 int line_pos = start_line_pos;
1229 while (line_pos < line_pos_end) {
1230 int lop = 8 / bits_per_one;
1231 int mask_per_one = (1 << bits_per_one) - 1;
1235 if ((mask & mask_per_one)) {
1237 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1238 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1239 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1243 mask >>= bits_per_one;
1246 /* at least one bit of ascii was one -> turn ascii on */
1249 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1250 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1251 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1263 } else { /* mask starting from end (big-endian) */
1264 while (start_line <= stop_line) {
1265 int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
1266 int line_pos = stop_line_pos-1;
1268 while (line_pos >= line_pos_start) {
1269 int lop = 8 / bits_per_one;
1270 int mask_per_one = (1 << bits_per_one) - 1;
1274 if ((mask & mask_per_one)) {
1276 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
1277 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
1278 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1282 mask >>= bits_per_one;
1285 /* at least one bit of ascii was one -> turn ascii on */
1288 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
1289 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
1290 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1299 stop_line_pos = per_line;
1304 return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
1309 /* Update the progress bar this many times when reading a file. */
1310 #define N_PROGBAR_UPDATES 100
1311 /* The minimum packet length required to check if a progress bar is needed or not */
1312 #define MIN_PACKET_LENGTH 1024
1315 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1316 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1317 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1318 * presumably why there's a progress bar for it.)
1320 * Perhaps what's needed is a custom widget (either one that lets you stuff
1321 * text into it more quickly, or one that's a "virtual" widget so that the
1322 * text for a row is constructed, via a callback, when the row is to be
1323 * displayed). A custom widget might also let us treat the offset, hex
1324 * data, and ASCII data as three columns, so you can select purely in
1325 * the hex dump column.
1328 packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
1330 int i = 0, j, k = 0, b, cur;
1331 guchar line[MAX_LINES_LEN + 1];
1332 static guchar hexchars[16] = {
1333 '0', '1', '2', '3', '4', '5', '6', '7',
1334 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1335 static const guint8 bitmask[8] = {
1336 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1338 unsigned int use_digits;
1341 progdlg_t *progbar = NULL;
1343 gboolean progbar_stop_flag;
1344 GTimeVal progbar_start_time;
1345 gchar progbar_status_str[100];
1346 int progbar_nextstep;
1347 int progbar_quantum;
1349 gtk_text_buffer_set_text(buf, "", 0);
1350 gtk_text_buffer_get_start_iter(buf, &iter);
1353 * How many of the leading digits of the offset will we supply?
1354 * We always supply at least 4 digits, but if the maximum offset
1355 * won't fit in 4 digits, we use as many digits as will be needed.
1357 if (((len - 1) & 0xF0000000) != 0)
1358 use_digits = 8; /* need all 8 digits */
1359 else if (((len - 1) & 0x0F000000) != 0)
1360 use_digits = 7; /* need 7 digits */
1361 else if (((len - 1) & 0x00F00000) != 0)
1362 use_digits = 6; /* need 6 digits */
1363 else if (((len - 1) & 0x000F0000) != 0)
1364 use_digits = 5; /* need 5 digits */
1366 use_digits = 4; /* we'll supply 4 digits */
1368 /* Record the number of digits in this text view. */
1369 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1371 /* Update the progress bar when it gets to this value. */
1372 if (len > MIN_PACKET_LENGTH){
1373 progbar_nextstep = 0;
1375 /* If length =< MIN_PACKET_LENGTH
1376 * there is no need to calculate the progress
1378 progbar_nextstep = len+1;
1381 /* When we reach the value that triggers a progress bar update,
1382 bump that value by this amount. */
1383 progbar_quantum = len/N_PROGBAR_UPDATES;
1384 /* Progress so far. */
1387 progbar_stop_flag = FALSE;
1388 g_get_current_time(&progbar_start_time);
1392 /* Create the progress bar if necessary.
1393 We check on every iteration of the loop, so that it takes no
1394 longer than the standard time to create it (otherwise, for a
1395 large packet, we might take considerably longer than that standard
1396 time in order to get to the next progress bar step). */
1397 if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1398 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1401 &progbar_start_time,
1404 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1405 when we update it, we have to run the GTK+ main loop to get it
1406 to repaint what's pending, and doing so may involve an "ioctl()"
1407 to see if there's any pending input from an X server, and doing
1408 that for every packet can be costly, especially on a big file. */
1409 if (i >= progbar_nextstep) {
1411 if (progbar != NULL) {
1412 /* let's not divide by zero. I should never be started
1413 * with count == 0, so let's assert that
1416 progbar_val = (gfloat) i / len;
1417 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1418 "%4u of %u bytes", i, len);
1419 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1422 progbar_nextstep += progbar_quantum;
1425 if (progbar_stop_flag) {
1426 /* Well, the user decided to abort the operation. Just stop,
1427 and arrange to return TRUE to our caller, so they know it
1428 was stopped explicitly. */
1432 /* Print the line number */
1436 c = (i >> (j*4)) & 0xF;
1437 line[cur++] = hexchars[c];
1443 switch (recent.gui_bytes_view) {
1445 k = i + BYTES_PER_LINE;
1448 k = i + BITS_PER_LINE;
1451 g_assert_not_reached();
1453 /* Print the hex bit */
1456 switch (recent.gui_bytes_view) {
1458 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1459 line[cur++] = hexchars[pd[i] & 0x0f];
1462 for (b = 0; b < 8; b++) {
1463 line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1467 g_assert_not_reached();
1470 switch (recent.gui_bytes_view) {
1472 line[cur++] = ' '; line[cur++] = ' ';
1475 for (b = 0; b < 8; b++) {
1480 g_assert_not_reached();
1484 /* Inter byte space if not at end of line */
1487 /* insert a space every BYTE_VIEW_SEP bytes */
1488 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1494 /* Print some space at the end of the line */
1495 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1497 /* Print the ASCII bit */
1502 if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
1505 else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
1506 c = EBCDIC_to_ASCII1(pd[i]);
1509 g_assert_not_reached();
1511 line[cur++] = isprint(c) ? c : '.';
1517 /* insert a space every BYTE_VIEW_SEP bytes */
1518 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1524 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1525 gtk_text_buffer_insert(buf, &iter, line, cur);
1530 /* We're done printing the packets; destroy the progress bar if
1532 if (progbar != NULL)
1533 destroy_progress_dlg(progbar);
1536 gtk_text_buffer_insert(buf, &iter, line, cur);
1541 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1542 int bend, guint32 bmask, int bmask_le,
1543 int astart, int aend, int encoding)
1545 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1546 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1550 GtkTextIter start, end;
1555 /* XXX: Setting the text_view buffer to NULL is apparently
1556 * not a good idea; If a progress_bar is displayed below
1557 * in delayed_create_progress_dlg() there will then be
1558 * a crash internally in the gtk library.
1559 * (It appears that gtk_text_view_set_buffer
1560 * queues a callback to be run when this
1561 * thread is next idle. Unfortunately the call to
1562 * gtk_main_iteration() in delayed_create_progress_dlg()
1563 * causes the internal callback to be run which then
1564 * crashes (because the textview has no buffer ?))
1566 gtk_text_view_set_buffer(bv_text_view, NULL);
1569 /* attach a dummy buffer in place of the real buffer.
1570 * (XXX: Presumably this is done so there's no attempt
1571 * to display the real buffer until it has been
1572 * completely generated).
1574 gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
1576 packet_hex_print_common(buf, bv, pd, len, encoding);
1578 /* mark everything with "plain" tag */
1579 gtk_text_buffer_get_start_iter(buf, &start);
1580 gtk_text_buffer_get_end_iter(buf, &end);
1581 gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
1583 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
1585 /* mark reverse tags */
1586 mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
1587 packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
1589 gtk_text_view_set_buffer(bv_text_view, buf);
1591 /* scroll text into position */
1593 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1594 gtk_text_buffer_delete_mark(buf, mark);
1596 g_object_unref(buf);
1600 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1601 field_info *finfo, guint len)
1603 /* do the initial printing and save the information needed */
1604 /* to redraw the display if preferences change. */
1606 int bstart = -1, bend = -1, blen = -1;
1607 guint32 bmask = 0x00; int bmask_le = 0;
1608 int astart = -1, aend = -1, alen = -1;
1611 if (finfo != NULL) {
1613 if (cfile.search_in_progress && (cfile.hex || (cfile.string && cfile.packet_data))) {
1614 /* In the hex view, only highlight the target bytes or string. The entire
1615 field can then be displayed by clicking on any of the bytes in the field. */
1617 blen = (int)strlen(cfile.sfilter)/2;
1619 blen = (int)strlen(cfile.sfilter);
1621 bstart = cfile.search_pos - (blen-1);
1624 blen = finfo->length;
1625 bstart = finfo->start;
1628 /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
1629 if (finfo->hfinfo) bmask = finfo->hfinfo->bitmask;
1630 astart = finfo->appendix_start;
1631 alen = finfo->appendix_length;
1633 if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1635 else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1637 else { /* unknown endianess - disable mask
1638 bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1643 if (bmask == 0x00) {
1644 int bito = FI_GET_BITS_OFFSET(finfo);
1645 int bitc = FI_GET_BITS_SIZE(finfo);
1646 int bitt = bito + bitc;
1648 /* construct mask using bito & bitc */
1649 /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
1650 if (bitt > 0 && bitt < 32) {
1652 bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
1653 bmask_le = 0; /* ? */
1658 if (bstart >= 0 && blen > 0) {
1659 bend = bstart + blen;
1661 if (astart >= 0 && alen > 0) {
1662 aend = astart + alen;
1665 if (bend == -1 && aend != -1) {
1672 /* don't exceed the end of available data */
1673 if (aend != -1 && (guint)aend > len) aend = len;
1674 if (bend != -1 && (guint)bend > len) bend = len;
1676 /* save the information needed to redraw the text */
1677 /* should we save the fd & finfo pointers instead ?? */
1678 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1679 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1680 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1681 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1682 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1683 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1684 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1685 GUINT_TO_POINTER((guint)fd->flags.encoding));
1687 /* stig: it should be done only for bitview... */
1688 if (recent.gui_bytes_view != BYTES_BITS)
1690 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1694 packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd, int offset, int bitoffset, guint len)
1696 /* do the initial printing and save the information needed */
1697 /* to redraw the display if preferences change. */
1699 int bstart = offset, bend = (bstart != -1) ? offset+1 : -1;
1700 guint32 bmask; int bmask_le = 0;
1701 int astart = -1, aend = -1;
1703 switch (recent.gui_bytes_view) {
1705 bmask = (bitoffset == 0) ? 0xf0 : (bitoffset == 4) ? 0x0f : 0xff;
1709 bmask = (1 << (7-bitoffset));
1714 g_assert_not_reached();
1718 /* save the information needed to redraw the text */
1719 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1720 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1721 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1722 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1723 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1724 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1725 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1726 GUINT_TO_POINTER((guint)fd->flags.encoding));
1728 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1732 * Redraw the text using the saved information; usually called if
1733 * the preferences have changed.
1736 packet_hex_reprint(GtkWidget *bv)
1738 int start, end, mask, mask_le, encoding;
1743 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1744 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1745 mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1746 mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1747 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1748 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1749 data = get_byte_view_data_and_length(bv, &len);
1750 g_assert(data != NULL);
1751 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1753 /* stig: it should be done only for bitview... */
1754 if (recent.gui_bytes_view != BYTES_BITS)
1756 packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1759 /* List of all protocol tree widgets, so we can globally set the selection
1760 mode and font of all of them. */
1761 static GList *ptree_widgets;
1763 /* Add a protocol tree widget to the list of protocol tree widgets. */
1764 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1767 remember_ptree_widget(GtkWidget *ptreew)
1769 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1771 /* Catch the "destroy" event on the widget, so that we remove it from
1772 the list when it's destroyed. */
1773 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1776 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1778 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1780 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1783 /* Set the selection mode of a given packet tree window. */
1785 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1787 GtkTreeSelection *selection;
1789 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1790 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1791 I think "browse" in Wireshark makes more sense than "SINGLE" in
1794 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1797 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1802 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1804 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1807 /* Set the selection mode of all packet tree windows. */
1809 set_ptree_sel_browse_all(gboolean val)
1811 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1815 set_ptree_font_cb(gpointer data, gpointer user_data)
1817 #if GTK_CHECK_VERSION(3,0,0)
1818 gtk_widget_override_font((GtkWidget *)data,
1819 (PangoFontDescription *)user_data);
1821 gtk_widget_modify_font((GtkWidget *)data,
1822 (PangoFontDescription *)user_data);
1827 set_ptree_font_all(PangoFontDescription *font)
1829 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1834 * Each expert_color_* level below should match the light gradient
1835 * colors in image/expert_indicators.svg.
1837 static gboolean colors_ok = FALSE;
1839 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1840 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1841 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1842 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1843 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1844 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1846 gchar *expert_color_chat_str;
1847 gchar *expert_color_note_str;
1848 gchar *expert_color_warn_str;
1849 gchar *expert_color_error_str;
1850 gchar *expert_color_foreground_str;
1852 void proto_draw_colors_init(void)
1858 /* Allocating collor isn't necessary? */
1859 get_color(&expert_color_chat);
1860 get_color(&expert_color_note);
1861 get_color(&expert_color_warn);
1862 get_color(&expert_color_error);
1863 get_color(&expert_color_foreground);
1865 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1866 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1867 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1868 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1869 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1872 get_color(&hidden_proto_item);
1879 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1880 GtkTreeModel *tree_model, GtkTreeIter *iter,
1885 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1888 proto_draw_colors_init();
1891 /* for the various possible attributes, see:
1892 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1894 * color definitions can be found at:
1895 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1896 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1899 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1900 * weight/style: doesn't take any effect
1903 /* for each field, we have to reset the renderer attributes */
1904 g_object_set (cell, "foreground-set", FALSE, NULL);
1906 g_object_set (cell, "background-set", FALSE, NULL);
1908 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1909 g_object_set (cell, "underline-set", FALSE, NULL);
1911 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1912 g_object_set (cell, "style-set", FALSE, NULL);*/
1914 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1915 g_object_set (cell, "weight-set", FALSE, NULL);*/
1917 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1918 /* we use "[...]" to mark generated items, no need to change things here */
1920 /* as some fonts don't support italic, don't use this */
1921 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1922 g_object_set (cell, "style-set", TRUE, NULL);
1924 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1925 g_object_set (cell, "weight-set", TRUE, NULL);*/
1928 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1929 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1930 g_object_set (cell, "foreground-set", TRUE, NULL);
1933 if (fi && fi->hfinfo) {
1934 if(fi->hfinfo->type == FT_PROTOCOL) {
1935 g_object_set (cell, "background", "gray90", NULL);
1936 g_object_set (cell, "background-set", TRUE, NULL);
1937 g_object_set (cell, "foreground", "black", NULL);
1938 g_object_set (cell, "foreground-set", TRUE, NULL);
1939 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1940 g_object_set (cell, "weight-set", TRUE, NULL);*/
1943 if((fi->hfinfo->type == FT_FRAMENUM) ||
1944 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1945 render_as_url(cell);
1949 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1950 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1952 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1953 g_object_set (cell, "background-set", TRUE, NULL);
1956 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1957 g_object_set (cell, "background-set", TRUE, NULL);
1960 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1961 g_object_set (cell, "background-set", TRUE, NULL);
1964 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1965 g_object_set (cell, "background-set", TRUE, NULL);
1968 g_assert_not_reached();
1970 g_object_set (cell, "foreground", "black", NULL);
1971 g_object_set (cell, "foreground-set", TRUE, NULL);
1976 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1978 GtkWidget *tv_scrollw, *tree_view;
1979 GtkTreeStore *store;
1980 GtkCellRenderer *renderer;
1981 GtkTreeViewColumn *column;
1985 tv_scrollw = scrolled_window_new(NULL, NULL);
1986 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1989 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1990 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1991 g_object_unref(G_OBJECT(store));
1992 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1993 renderer = gtk_cell_renderer_text_new();
1994 g_object_set (renderer, "ypad", 0, NULL);
1995 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1996 -1, "Name", renderer,
1998 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
2000 gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
2003 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
2004 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2005 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
2006 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
2007 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
2008 set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
2009 #if GTK_CHECK_VERSION(3,0,0)
2010 gtk_widget_override_font(tree_view, user_font_get_regular());
2012 gtk_widget_modify_font(tree_view, user_font_get_regular());
2014 remember_ptree_widget(tree_view);
2016 *tree_view_p = tree_view;
2022 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
2025 for(i=0; i < num_tree_types; i++) {
2026 tree_is_expanded[i] = TRUE;
2028 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
2032 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
2035 for(i=0; i < num_tree_types; i++) {
2036 tree_is_expanded[i] = FALSE;
2038 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
2042 struct proto_tree_draw_info {
2043 GtkTreeView *tree_view;
2048 main_proto_tree_draw(proto_tree *protocol_tree)
2050 proto_tree_draw(protocol_tree, tree_view_gbl);
2055 tree_view_follow_link(field_info *fi)
2059 if(fi->hfinfo->type == FT_FRAMENUM) {
2060 cf_goto_frame(&cfile, fi->value.value.uinteger);
2062 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
2063 url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL);
2065 browser_open_url(url);
2072 /* If the user selected a position in the tree view, try to find
2073 * the item in the GUI proto_tree that corresponds to that byte, and
2076 tree_view_select(GtkWidget *widget, GdkEventButton *event)
2078 GtkTreeSelection *sel;
2081 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
2082 (gint) (((GdkEventButton *)event)->x),
2083 (gint) (((GdkEventButton *)event)->y),
2084 &path, NULL, NULL, NULL))
2086 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2088 /* if that's a doubleclick, try to follow the link */
2089 if(event->type == GDK_2BUTTON_PRESS) {
2090 GtkTreeModel *model;
2094 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
2095 gtk_tree_model_get(model, &iter, 1, &fi, -1);
2096 tree_view_follow_link(fi);
2099 else if (((GdkEventButton *)event)->button != 1) {
2100 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
2101 gtk_tree_selection_select_path(sel, path);
2109 /* fill the whole protocol tree with the string values */
2111 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
2113 GtkTreeStore *store;
2114 struct proto_tree_draw_info info;
2116 info.tree_view = GTK_TREE_VIEW(tree_view);
2119 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
2122 * Clear out any crud left over in the display of the protocol
2123 * tree, by removing all nodes from the tree.
2124 * This is how it's done in testgtk.c in GTK+.
2126 gtk_tree_store_clear(store);
2128 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2132 /* fill a single protocol tree item with the string value */
2134 proto_tree_draw_node(proto_node *node, gpointer data)
2136 struct proto_tree_draw_info info;
2137 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2139 field_info *fi = PNODE_FINFO(node);
2140 gchar label_str[ITEM_LABEL_LENGTH];
2142 gboolean is_leaf, is_expanded;
2143 GtkTreeStore *store;
2147 g_assert(fi && "dissection with an invisible proto tree?");
2149 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2152 /* was a free format label produced? */
2154 label_ptr = fi->rep->representation;
2156 else { /* no, make a generic label */
2157 label_ptr = label_str;
2158 proto_item_fill_label(fi, label_str);
2161 if (node->first_child != NULL) {
2163 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2164 if (tree_is_expanded[fi->tree_type]) {
2168 is_expanded = FALSE;
2173 is_expanded = FALSE;
2176 if (PROTO_ITEM_IS_GENERATED(node)) {
2177 if (PROTO_ITEM_IS_HIDDEN(node)) {
2178 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2180 label_ptr = g_strdup_printf("[%s]", label_ptr);
2182 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2183 label_ptr = g_strdup_printf("<%s>", label_ptr);
2186 info.tree_view = parent_info->tree_view;
2187 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2188 gtk_tree_store_append(store, &iter, parent_info->iter);
2189 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2191 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2197 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2198 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2200 gtk_tree_view_expand_to_path(info.tree_view, path);
2202 gtk_tree_view_collapse_row(info.tree_view, path);
2203 gtk_tree_path_free(path);
2208 * Clear the hex dump and protocol tree panes.
2211 clear_tree_and_hex_views(void)
2213 /* Clear the hex dump by getting rid of all the byte views. */
2214 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2215 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2217 /* Add a placeholder byte view so that there's at least something
2218 displayed in the byte view notebook. */
2219 add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2221 /* Clear the protocol tree by removing all nodes in the ctree.
2222 This is how it's done in testgtk.c in GTK+ */
2223 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2227 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2229 if (recent.gui_bytes_view != view) {
2230 recent.gui_bytes_view = view;
2231 redraw_packet_bytes_all();