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>
47 #include <epan/epan_dissect.h>
49 #include <epan/packet.h>
50 #include <epan/charsets.h>
51 #include <epan/prefs.h>
52 #include <epan/filesystem.h>
54 #include "../isprint.h"
55 #include "../alert_box.h"
56 #include "../simple_dialog.h"
57 #include "../progress_dlg.h"
58 #include "../ui_util.h"
59 #include <wsutil/file_util.h>
62 #include "gtk/color_utils.h"
63 #include "gtk/capture_file_dlg.h"
64 #include "gtk/packet_win.h"
65 #include "gtk/file_dlg.h"
66 #include "gtk/gui_utils.h"
67 #include "gtk/gtkglobals.h"
68 #include "gtk/font_utils.h"
69 #include "gtk/webbrowser.h"
71 #include "gtk/menus.h"
72 #include "gtk/main_proto_draw.h"
73 #include "gtk/recent.h"
76 #include <gdk/gdkwin32.h>
78 #include "file_dlg_win32.h"
82 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
83 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
84 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
85 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
86 #define E_BYTE_VIEW_START_KEY "byte_view_start"
87 #define E_BYTE_VIEW_END_KEY "byte_view_end"
88 #define E_BYTE_VIEW_MASK_KEY "byte_view_mask"
89 #define E_BYTE_VIEW_MASKLE_KEY "byte_view_mask_le"
90 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
91 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
92 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
95 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
96 proto_tree *tree, GtkWidget *tree_view);
99 proto_tree_draw_node(proto_node *node, gpointer data);
101 /* Get the current text window for the notebook. */
103 get_notebook_bv_ptr(GtkWidget *nb_ptr)
108 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
109 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
111 return GTK_BIN(bv_page)->child;
117 * Get the data and length for a byte view, given the byte view page.
118 * Return the pointer, or NULL on error, and set "*data_len" to the length.
121 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
123 tvbuff_t *byte_view_tvb;
124 const guint8 *data_ptr;
126 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
127 if (byte_view_tvb == NULL)
130 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
131 *data_len = tvb_length(byte_view_tvb);
136 * Set the current text window for the notebook to the window that
137 * refers to a particular tvbuff.
140 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
143 GtkWidget *bv_page, *bv;
147 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
149 bv = GTK_BIN(bv_page)->child;
150 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
153 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
159 /* Redraw a given byte view window. */
161 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
167 bv = get_notebook_bv_ptr(nb);
169 data = get_byte_view_data_and_length(bv, &len);
171 packet_hex_print(bv, data, fd, finfo, len);
175 /* Redraw all byte view windows. */
177 redraw_packet_bytes_all(void)
179 if (cfile.current_frame != NULL)
180 redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
182 redraw_packet_bytes_packet_wins();
184 /* XXX - this is a hack, to workaround a bug in GTK2.x!
185 when changing the font size, even refilling of the corresponding
186 gtk_text_buffer doesn't seem to trigger an update.
187 The only workaround is to freshly select the frame, which will remove any
188 existing notebook tabs and "restart" the whole byte view again. */
189 if (cfile.current_frame != NULL) {
190 cfile.current_row = -1;
191 cf_goto_frame(&cfile, cfile.current_frame->num);
196 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
197 GtkTreePath *path _U_, gpointer user_data _U_)
202 model = gtk_tree_view_get_model(tree_view);
203 gtk_tree_model_get(model, iter, 1, &finfo, -1);
207 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
208 * are thus presumably leaf nodes and cannot be expanded.
210 if (finfo->tree_type != -1) {
211 g_assert(finfo->tree_type >= 0 &&
212 finfo->tree_type < num_tree_types);
213 tree_is_expanded[finfo->tree_type] = TRUE;
218 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
219 GtkTreePath *path _U_, gpointer user_data _U_)
224 model = gtk_tree_view_get_model(tree_view);
225 gtk_tree_model_get(model, iter, 1, &finfo, -1);
229 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
230 * are thus presumably leaf nodes and cannot be collapsed.
232 if (finfo->tree_type != -1) {
233 g_assert(finfo->tree_type >= 0 &&
234 finfo->tree_type < num_tree_types);
235 tree_is_expanded[finfo->tree_type] = FALSE;
239 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
240 #define BYTES_PER_LINE 16 /* max byte values in a line */
241 #define BITS_PER_LINE 8 /* max bit values in a line */
242 #define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
243 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
244 /* max number of characters hex dump takes -
245 2 digits plus trailing blank
246 plus separator between first and
248 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
249 /* number of characters those bytes take;
250 3 characters per byte of hex dump,
251 2 blanks separating hex from ASCII,
252 1 character per byte of ASCII dump */
253 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
254 /* number of characters per line;
255 offset, 2 blanks separating offset
256 from data dump, data dump */
257 #define MAX_LINES 100
258 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
260 /* Which byte the offset is referring to. Associates
261 * whitespace with the preceding digits. */
263 byte_num(int offset, int start_point)
265 return (offset - start_point) / 3;
268 bit_num(int offset, int start_point)
270 return (offset - start_point) / 9;
273 struct field_lookup_info {
279 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
283 struct field_lookup_info *fli = (struct field_lookup_info *)data;
285 gtk_tree_model_get(model, iter, 1, &fi, -1);
294 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
297 struct field_lookup_info fli;
299 g_assert(finfo != NULL);
301 model = gtk_tree_view_get_model(tree_view);
303 gtk_tree_model_foreach(model, lookup_finfo, &fli);
305 return gtk_tree_model_get_path(model, &fli.iter);
309 hex_view_get_byte(guint ndigits, int row, int column)
322 * The column of the first hex digit in the first half.
323 * That starts after "ndigits" digits of offset and two
326 digits_start_1 = ndigits + 2;
329 * The column of the last hex digit in the first half.
330 * There are BYTES_PER_LINE/2 bytes displayed in the first
331 * half; there are 2 characters per byte, plus a separating
332 * blank after all but the last byte's characters.
334 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
335 (BYTES_PER_LINE/2 - 1);
338 * The column of the first hex digit in the second half.
339 * Add 2 for the 2 separating blanks between the halves.
341 digits_start_2 = digits_end_1 + 2;
344 * The column of the last hex digit in the second half.
345 * Add the same value we used to get "digits_end_1" from
348 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
349 (BYTES_PER_LINE/2 - 1);
352 * The column of the first "text dump" character in the first half.
353 * Add 3 for the 3 separating blanks between the hex and text dump.
355 text_start_1 = digits_end_2 + 3;
358 * The column of the last "text dump" character in the first half.
359 * There are BYTES_PER_LINE/2 bytes displayed in the first
360 * half; there is 1 character per byte.
362 * Then subtract 1 to get the last column of the first half
363 * rather than the first column after the first half.
365 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
368 * The column of the first "text dump" character in the second half.
369 * Add back the 1 to get the first column after the first half,
370 * and then add 1 for the separating blank between the halves.
372 text_start_2 = text_end_1 + 2;
375 * The column of the last "text dump" character in second half.
376 * Add the same value we used to get "text_end_1" from
379 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
381 /* Given the column and row, determine which byte offset
382 * the user clicked on. */
383 if (column >= digits_start_1 && column <= digits_end_1) {
384 byte = byte_num(column, digits_start_1);
389 else if (column >= digits_start_2 && column <= digits_end_2) {
390 byte = byte_num(column, digits_start_2);
396 else if (column >= text_start_1 && column <= text_end_1) {
397 byte = column - text_start_1;
399 else if (column >= text_start_2 && column <= text_end_2) {
400 byte = 8 + column - text_start_2;
403 /* The user didn't select a hex digit or
404 * text-dump character. */
408 /* Add the number of bytes from the previous rows. */
409 byte += row * BYTES_PER_LINE;
415 bit_view_get_byte(guint ndigits, int row, int column)
424 * The column of the first bit digit.
425 * That starts after "ndigits" digits of offset and two
428 digits_start = ndigits + 2;
431 * The column of the last bit digit.
432 * There are BITS_PER_LINE bytes displayed; there are
433 * 8 characters per byte, plus a separating blank
434 * after all but the last byte's characters.
436 digits_end = digits_start + (BITS_PER_LINE)*8 +
440 * The column of the first "text dump" character.
441 * Add 3 for the 3 separating blanks between the bit and text dump.
443 text_start = digits_end + 3;
446 * The column of the last "text dump" character.
447 * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
449 * Then subtract 1 to get the last column.
451 text_end = text_start + BITS_PER_LINE - 1;
453 /* Given the column and row, determine which byte offset
454 * the user clicked on. */
455 if (column >= digits_start && column <= digits_end) {
456 byte = bit_num(column, digits_start);
461 else if (column >= text_start && column <= text_end) {
462 byte = column - text_start;
465 /* The user didn't select a hex digit or
466 * text-dump character. */
470 /* Add the number of bytes from the previous rows. */
471 byte += row * BITS_PER_LINE;
476 /* If the user selected a certain byte in the byte view, try to find
477 * the item in the GUI proto_tree that corresponds to that byte, and:
479 * if we succeed, select it, and return TRUE;
480 * if we fail, return FALSE. */
482 byte_view_select(GtkWidget *widget, GdkEventButton *event)
484 GtkTextView *bv = GTK_TEXT_VIEW(widget);
486 GtkTreeView *tree_view;
494 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
497 * Somebody clicked on the dummy byte view; do nothing.
501 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
502 E_BYTE_VIEW_TREE_VIEW_PTR));
504 /* get the row/column selected */
505 gtk_text_view_window_to_buffer_coords(bv,
506 gtk_text_view_get_window_type(bv, event->window),
507 (gint) event->x, (gint) event->y, &x, &y);
508 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
509 row = gtk_text_iter_get_line(&iter);
510 column = gtk_text_iter_get_line_offset(&iter);
513 * Get the number of digits of offset being displayed, and
514 * compute the byte position in the buffer.
516 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
518 switch (recent.gui_bytes_view) {
520 byte = hex_view_get_byte(ndigits, row, column);
523 byte = bit_view_get_byte(ndigits, row, column);
526 g_assert_not_reached();
533 /* Get the data source tvbuff */
534 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
536 return highlight_field(tvb, byte, tree_view, tree);
539 /* This highlights the field in the proto tree that is at position byte */
541 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
545 GtkTreePath *first_path, *path;
547 struct field_lookup_info fli;
550 /* Find the finfo that corresponds to our byte. */
551 finfo = proto_find_field_from_offset(tree, byte, tvb);
557 model = gtk_tree_view_get_model(tree_view);
559 gtk_tree_model_foreach(model, lookup_finfo, &fli);
561 /* Expand our field's row */
562 first_path = gtk_tree_model_get_path(model, &fli.iter);
563 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
564 expand_tree(tree_view, &fli.iter, NULL, NULL);
566 /* ... and its parents */
567 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
568 path = gtk_tree_model_get_path(model, &parent);
569 gtk_tree_view_expand_row(tree_view, path, FALSE);
570 expand_tree(tree_view, &parent, NULL, NULL);
572 gtk_tree_path_free(path);
575 /* select our field's row */
576 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
579 /* And position the window so the selection is visible.
580 * Position the selection in the middle of the viewable
582 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
584 gtk_tree_path_free(first_path);
589 /* Calls functions for different mouse-button presses. */
591 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
593 GdkEventButton *event_button = NULL;
595 if(widget == NULL || event == NULL || data == NULL) {
599 if(event->type == GDK_BUTTON_PRESS) {
600 event_button = (GdkEventButton *) event;
602 /* To qoute the "Gdk Event Structures" doc:
603 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
604 switch(event_button->button) {
607 return byte_view_select(widget, event_button);
609 return popup_menu_handler(widget, event, data);
623 byte_nb = gtk_notebook_new();
624 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
626 /* this will only have an effect, if no tabs are shown */
627 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
629 /* set the tabs scrollable, if they don't fit into the pane */
630 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
632 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
633 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
635 /* Add a placeholder byte view so that there's at least something
636 displayed in the byte view notebook. */
637 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
643 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
645 const guint8 *byte_data;
648 byte_data = get_byte_view_data_and_length(bv, &byte_len);
649 if (byte_data == NULL) {
650 /* This must be the dummy byte view if no packet is selected. */
653 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
657 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
658 proto_tree *tree, GtkWidget *tree_view)
660 GtkWidget *byte_view, *byte_scrollw, *label;
664 /* Byte view. Create a scrolled window for the text. */
665 byte_scrollw = scrolled_window_new(NULL, NULL);
666 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
668 /* Add scrolled pane to tabbed window */
669 label = gtk_label_new(name);
670 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
672 gtk_widget_show(byte_scrollw);
674 byte_view = gtk_text_view_new();
675 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
676 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
677 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
679 #ifdef NEW_PACKET_LIST
680 style = gtk_widget_get_style(GTK_WIDGET(top_level));
682 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
684 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
685 gtk_text_buffer_create_tag(buf, "reverse",
686 "font-desc", user_font_get_regular(),
687 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
688 "background-gdk", &style->base[GTK_STATE_SELECTED],
691 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
692 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
693 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
695 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
696 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
697 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
699 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
700 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
702 gtk_widget_show(byte_view);
704 /* no tabs if this is the first page */
705 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
706 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
708 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
710 /* set this page (this will print the packet data) */
711 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
712 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
718 add_main_byte_views(epan_dissect_t *edt)
720 add_byte_views(edt, tree_view_gbl, byte_nb_ptr_gbl);
724 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
725 GtkWidget *byte_nb_ptr)
731 * Get rid of all the old notebook tabs.
733 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
734 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
737 * Add to the specified byte view notebook tabs for hex dumps
738 * of all the data sources for the specified frame.
740 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
742 add_byte_tab(byte_nb_ptr, get_data_source_name(src), src->tvb, edt->tree,
747 * Initially select the first byte view.
749 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
754 static GtkWidget *savehex_dlg=NULL;
757 savehex_dlg_destroy_cb(void)
764 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
766 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
768 gboolean end_of_line = TRUE; /* Initial state is end of line */
769 int byte_line_part_length;
774 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
775 hex_str = g_string_new("");
776 char_str= g_string_new("");
781 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
784 g_string_append_printf(hex_str," %02x",*data_p);
786 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
791 /* Look ahead to see if this is the end of the data */
792 byte_line_part_length = (++i) % byte_line_length;
794 /* End of data - need to fill in spaces in hex string and then do "end of line".
797 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
798 g_string_append(hex_str," "); /* Three spaces for each missing byte */
802 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
808 g_string_append(copy_buffer, hex_str->str);
810 /* Two spaces between hex and text */
811 g_string_append_c(copy_buffer, ' ');
812 g_string_append_c(copy_buffer, ' ');
813 g_string_append(copy_buffer, char_str->str);
815 /* Setup ready for next line */
816 g_string_assign(char_str,"");
817 g_string_assign(hex_str, "\n");
821 g_string_free(hex_str, TRUE);
822 g_string_free(char_str, TRUE);
826 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
831 /* Copy printable characters, newlines, and (horizontal) tabs. */
832 if(isprint(*data_p)) {
834 } else if(*data_p==0x0a) {
836 } else if(*data_p==0x09) {
839 return 1; /* Just ignore non-printable bytes */
841 g_string_append_c(copy_buffer,to_append);
846 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
848 g_string_append_printf(copy_buffer, "%02x", *data_p);
853 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
858 int bytes_consumed = 0;
861 const guint8* data_p;
863 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
865 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
867 /* shouldn't happen */
868 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
872 data_p = get_byte_view_data_and_length(bv, &len);
873 g_assert(data_p != NULL);
875 flags = data_type & CD_FLAGSMASK;
876 data_type = data_type & CD_TYPEMASK;
878 if(flags & CD_FLAGS_SELECTEDONLY) {
881 /* Get the start and end of the highlighted bytes. */
882 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
883 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
885 if(start >= 0 && end > start && (end - start <= (int)len)) {
893 /* This is too different from other text formats - handle separately */
894 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
897 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
898 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
901 /* Completely different logic to text copies - leave copy buffer alone */
902 copy_binary_to_clipboard(data_p,len);
905 /* Incrementally write to text buffer in various formats */
909 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
912 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
915 g_assert_not_reached();
919 g_assert(bytes_consumed>0);
920 data_p += bytes_consumed;
921 len -= bytes_consumed;
926 if(copy_buffer->len > 0) {
927 copy_to_clipboard(copy_buffer);
930 g_string_free(copy_buffer, TRUE);
933 /* save the current highlighted hex data */
935 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
940 const guint8 *data_p = NULL;
943 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
945 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
946 if (!file ||! *file) {
947 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
952 if (test_for_directory(file) == EISDIR) {
953 /* It's a directory - set the file selection box to display that
954 directory, and leave the selection box displayed. */
955 set_last_open_dir(file);
957 file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
958 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
959 return FALSE; /* do gtk_dialog_run again */
962 /* XXX: Must check if file name exists first */
964 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
966 /* shouldn't happen */
967 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
972 * Retrieve the info we need
974 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
975 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
976 data_p = get_byte_view_data_and_length(bv, &len);
978 if (data_p == NULL || start == -1 || start > end) {
979 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
980 "No data selected to save!");
985 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
987 open_failure_alert_box(file, errno, TRUE);
991 if (ws_write(fd, data_p + start, end - start) < 0) {
992 write_failure_alert_box(file, errno);
997 if (ws_close(fd) < 0) {
998 write_failure_alert_box(file, errno);
1003 /* Get rid of the dialog box */
1005 #if 0 /* being handled by caller (for now) */
1006 window_destroy(GTK_WIDGET(savehex_dlg));
1011 /* Launch the dialog box to put up the file selection box etc */
1013 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1017 const guint8 *data_p = NULL;
1023 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
1027 /* don't show up the dialog, if no data has to be saved */
1028 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
1030 /* shouldn't happen */
1031 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1034 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1035 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1036 data_p = get_byte_view_data_and_length(bv, &len);
1038 if (data_p == NULL || start == -1 || start > end) {
1039 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1043 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
1044 /* if the window is already open, bring it to front */
1046 reactivate_window(savehex_dlg);
1051 * Build the dialog box we need.
1053 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1054 #if GTK_CHECK_VERSION(2,8,0)
1055 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
1059 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1060 end - start, plurality(end - start, "byte", "bytes"));
1061 dlg_lb = gtk_label_new(label);
1063 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1064 gtk_widget_show(dlg_lb);
1066 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
1069 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1070 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1072 window_destroy(savehex_dlg);
1075 /* "Run" the GtkFileChooserDialog. */
1076 /* Upon exit: If "Accept" run the OK callback. */
1077 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
1078 /* If not accept (ie: cancel) destroy the window. */
1079 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
1080 /* return with a TRUE status so that the dialog window will be destroyed. */
1081 /* Trying to re-run the dialog after popping up an alert box will not work */
1082 /* since the user will not be able to dismiss the alert box. */
1083 /* The (somewhat unfriendly) effect: the user must re-invoke the */
1084 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
1086 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
1087 /* GtkFileChooserDialog. */
1088 while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1089 if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
1090 break; /* we're done */
1093 window_destroy(savehex_dlg);
1096 static GtkTextMark *
1097 packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int start, int end, guint32 mask, int mask_le, int use_digits, int create_mark)
1099 GtkTextIter i_start, i_stop, iter;
1101 GtkTextTag *revstyle_tag;
1102 const char *revstyle;
1106 int bits_per_one = 0;
1107 int hex_offset, ascii_offset;
1109 int start_line, start_line_pos;
1110 int stop_line, stop_line_pos;
1112 if (start == -1 || end == -1)
1115 /* Display with inverse video ? */
1116 if (prefs.gui_hex_dump_highlight_style)
1117 revstyle = "reverse";
1121 revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle);
1123 switch (recent.gui_bytes_view) {
1125 per_line = BYTES_PER_LINE;
1126 per_one = 2+1; /* "ff " */
1130 per_line = BITS_PER_LINE;
1131 per_one = 8+1; /* "10101010 " */
1135 g_assert_not_reached();
1138 start_line = start / per_line;
1139 start_line_pos = start % per_line;
1141 stop_line = end / per_line;
1142 stop_line_pos = end % per_line;
1144 #define hex_fix(pos) hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1145 #define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line)
1147 hex_offset = use_digits + 2;
1148 ascii_offset = hex_fix(per_line) + 2;
1150 gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos));
1152 /* stig: it should be done only for bitview... */
1153 if (mask == 0x00 || recent.gui_bytes_view != BYTES_BITS) {
1154 while (start_line <= stop_line) {
1155 int end_line = (start_line == stop_line) ? stop_line_pos : per_line;
1157 if (start_line_pos == end_line) break;
1160 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
1161 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(end_line)-1);
1162 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1165 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
1166 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(end_line));
1167 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1174 /* XXX, merge & optimize? */
1176 /* XXX, Spaces are not highlighted - good thing or bad? */
1178 if (mask_le) /* LSB of mask first (little-endian) */
1179 while (start_line <= stop_line) {
1180 int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1181 int line_pos = start_line_pos;
1183 while (line_pos < line_pos_end) {
1184 int lop = 8 / bits_per_one;
1185 int mask_per_one = (1 << bits_per_one) - 1;
1189 if ((mask & mask_per_one)) {
1191 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1192 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1193 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1197 mask >>= bits_per_one;
1200 /* at least one bit of ascii was one -> turn ascii on */
1203 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1204 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1205 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1217 else /* mask starting from end (big-endian) */
1218 while (start_line <= stop_line) {
1219 int line_pos_start = (stop_line == start_line) ? start_line_pos : 0;
1220 int line_pos = stop_line_pos-1;
1222 while (line_pos >= line_pos_start) {
1223 int lop = 8 / bits_per_one;
1224 int mask_per_one = (1 << bits_per_one) - 1;
1228 if ((mask & mask_per_one)) {
1230 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1231 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1232 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1236 mask >>= bits_per_one;
1239 /* at least one bit of ascii was one -> turn ascii on */
1242 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1243 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1244 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1253 stop_line_pos = per_line;
1258 return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
1263 /* Update the progress bar this many times when reading a file. */
1264 #define N_PROGBAR_UPDATES 100
1265 /* The minimum packet length required to check if a progres bar is needed or not */
1266 #define MIN_PACKET_LENGTH 1024
1269 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1270 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1271 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1272 * presumably why there's a progress bar for it.)
1274 * Perhaps what's needed is a custom widget (either one that lets you stuff
1275 * text into it more quickly, or one that's a "virtual" widget so that the
1276 * text for a row is constructed, via a callback, when the row is to be
1277 * displayed). A custom widget might also let us treat the offset, hex
1278 * data, and ASCII data as three columns, so you can select purely in
1279 * the hex dump column.
1282 packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
1284 int i = 0, j, k = 0, b, cur;
1285 guchar line[MAX_LINES_LEN + 1];
1286 static guchar hexchars[16] = {
1287 '0', '1', '2', '3', '4', '5', '6', '7',
1288 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1289 static const guint8 bitmask[8] = {
1290 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1292 unsigned int use_digits;
1295 progdlg_t *progbar = NULL;
1297 gboolean progbar_stop_flag;
1298 GTimeVal progbar_start_time;
1299 gchar progbar_status_str[100];
1300 int progbar_nextstep;
1301 int progbar_quantum;
1303 gtk_text_buffer_set_text(buf, "", 0);
1304 gtk_text_buffer_get_start_iter(buf, &iter);
1307 * How many of the leading digits of the offset will we supply?
1308 * We always supply at least 4 digits, but if the maximum offset
1309 * won't fit in 4 digits, we use as many digits as will be needed.
1311 if (((len - 1) & 0xF0000000) != 0)
1312 use_digits = 8; /* need all 8 digits */
1313 else if (((len - 1) & 0x0F000000) != 0)
1314 use_digits = 7; /* need 7 digits */
1315 else if (((len - 1) & 0x00F00000) != 0)
1316 use_digits = 6; /* need 6 digits */
1317 else if (((len - 1) & 0x000F0000) != 0)
1318 use_digits = 5; /* need 5 digits */
1320 use_digits = 4; /* we'll supply 4 digits */
1322 /* Record the number of digits in this text view. */
1323 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1325 /* Update the progress bar when it gets to this value. */
1326 if (len > MIN_PACKET_LENGTH){
1327 progbar_nextstep = 0;
1329 /* If length =< MIN_PACKET_LENGTH
1330 * there is no need to calculate the progress
1332 progbar_nextstep = len+1;
1335 /* When we reach the value that triggers a progress bar update,
1336 bump that value by this amount. */
1337 progbar_quantum = len/N_PROGBAR_UPDATES;
1338 /* Progress so far. */
1341 progbar_stop_flag = FALSE;
1342 g_get_current_time(&progbar_start_time);
1346 /* Create the progress bar if necessary.
1347 We check on every iteration of the loop, so that it takes no
1348 longer than the standard time to create it (otherwise, for a
1349 large packet, we might take considerably longer than that standard
1350 time in order to get to the next progress bar step). */
1351 if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1352 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1355 &progbar_start_time,
1358 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1359 when we update it, we have to run the GTK+ main loop to get it
1360 to repaint what's pending, and doing so may involve an "ioctl()"
1361 to see if there's any pending input from an X server, and doing
1362 that for every packet can be costly, especially on a big file. */
1363 if (i >= progbar_nextstep) {
1365 if (progbar != NULL) {
1366 /* let's not divide by zero. I should never be started
1367 * with count == 0, so let's assert that
1370 progbar_val = (gfloat) i / len;
1371 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1372 "%4u of %u bytes", i, len);
1373 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1376 progbar_nextstep += progbar_quantum;
1379 if (progbar_stop_flag) {
1380 /* Well, the user decided to abort the operation. Just stop,
1381 and arrange to return TRUE to our caller, so they know it
1382 was stopped explicitly. */
1386 /* Print the line number */
1390 c = (i >> (j*4)) & 0xF;
1391 line[cur++] = hexchars[c];
1397 switch (recent.gui_bytes_view) {
1399 k = i + BYTES_PER_LINE;
1402 k = i + BITS_PER_LINE;
1405 g_assert_not_reached();
1407 /* Print the hex bit */
1410 switch (recent.gui_bytes_view) {
1412 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1413 line[cur++] = hexchars[pd[i] & 0x0f];
1416 for (b = 0; b < 8; b++) {
1417 line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1421 g_assert_not_reached();
1424 switch (recent.gui_bytes_view) {
1426 line[cur++] = ' '; line[cur++] = ' ';
1429 for (b = 0; b < 8; b++) {
1434 g_assert_not_reached();
1438 /* Inter byte space if not at end of line */
1441 /* insert a space every BYTE_VIEW_SEP bytes */
1442 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1448 /* Print some space at the end of the line */
1449 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1451 /* Print the ASCII bit */
1456 if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
1459 else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
1460 c = EBCDIC_to_ASCII1(pd[i]);
1463 g_assert_not_reached();
1465 line[cur++] = isprint(c) ? c : '.';
1471 /* insert a space every BYTE_VIEW_SEP bytes */
1472 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1478 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1479 gtk_text_buffer_insert(buf, &iter, line, cur);
1484 /* We're done printing the packets; destroy the progress bar if
1486 if (progbar != NULL)
1487 destroy_progress_dlg(progbar);
1490 gtk_text_buffer_insert(buf, &iter, line, cur);
1495 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1496 int bend, guint32 bmask, int bmask_le,
1497 int astart, int aend, int encoding)
1499 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1500 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1504 GtkTextIter start, end;
1509 /* XXX: Apparently not a good idea; If a progress_bar
1510 * is displayed below in delayed_create_progress_dlg()
1511 * there will then be a crash internally in the gtk library.
1512 * (It appears that gtk_text_view_set_buffer
1513 * queues a callback to be run when this
1514 * thread is next idle. Unfortunately the call to
1515 * gtk_main_iteration() in delayed_create_progress_dlg()
1516 * causes the internal callback to be run which then
1517 * crashes (because the textview has no buffer ?))
1519 gtk_text_view_set_buffer(bv_text_view, NULL);
1522 /* attach a dummy buffer in place of the real buffer.
1523 * (XXX: Presumably this is done so there's no attempt
1524 * to display the real buffer until it has been
1525 * completely generated).
1527 gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
1529 packet_hex_print_common(buf, bv, pd, len, encoding);
1531 /* mark everything with "plain" tag */
1532 gtk_text_buffer_get_start_iter(buf, &start);
1533 gtk_text_buffer_get_end_iter(buf, &end);
1534 gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
1536 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
1538 /* mark reverse tags */
1539 mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
1540 packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
1542 gtk_text_view_set_buffer(bv_text_view, buf);
1544 /* scroll text into position */
1546 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1547 gtk_text_buffer_delete_mark(buf, mark);
1549 g_object_unref(buf);
1553 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1554 field_info *finfo, guint len)
1556 /* do the initial printing and save the information needed */
1557 /* to redraw the display if preferences change. */
1559 int bstart = -1, bend = -1, blen = -1;
1560 guint32 bmask = 0x00; int bmask_le = 0;
1561 int astart = -1, aend = -1, alen = -1;
1563 if (finfo != NULL) {
1564 bstart = finfo->start;
1565 blen = finfo->length;
1566 /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
1567 bmask = finfo->hfinfo->bitmask;
1568 astart = finfo->appendix_start;
1569 alen = finfo->appendix_length;
1571 if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1573 else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1576 bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1579 if (bstart >= 0 && blen > 0) {
1580 bend = bstart + blen;
1582 if (astart >= 0 && alen > 0) {
1583 aend = astart + alen;
1586 if (bend == -1 && aend != -1) {
1593 /* save the information needed to redraw the text */
1594 /* should we save the fd & finfo pointers instead ?? */
1595 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1596 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1597 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1598 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1599 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1600 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1601 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1602 GUINT_TO_POINTER((guint)fd->flags.encoding));
1604 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1608 * Redraw the text using the saved information; usually called if
1609 * the preferences have changed.
1612 packet_hex_reprint(GtkWidget *bv)
1614 int start, end, mask, mask_le, encoding;
1619 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1620 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1621 mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1622 mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1623 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1624 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1625 data = get_byte_view_data_and_length(bv, &len);
1626 g_assert(data != NULL);
1627 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1629 packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1632 /* List of all protocol tree widgets, so we can globally set the selection
1633 mode and font of all of them. */
1634 static GList *ptree_widgets;
1636 /* Add a protocol tree widget to the list of protocol tree widgets. */
1637 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1640 remember_ptree_widget(GtkWidget *ptreew)
1642 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1644 /* Catch the "destroy" event on the widget, so that we remove it from
1645 the list when it's destroyed. */
1646 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1649 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1651 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1653 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1656 /* Set the selection mode of a given packet tree window. */
1658 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1660 GtkTreeSelection *selection;
1662 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1663 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1664 I think "browse" in Wireshark makes more sense than "SINGLE" in
1667 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1670 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1675 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1677 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1680 /* Set the selection mode of all packet tree windows. */
1682 set_ptree_sel_browse_all(gboolean val)
1684 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1688 set_ptree_font_cb(gpointer data, gpointer user_data)
1690 gtk_widget_modify_font((GtkWidget *)data,
1691 (PangoFontDescription *)user_data);
1695 set_ptree_font_all(PangoFontDescription *font)
1697 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1702 * Each expert_color_* level below should match the light gradient
1703 * colors in image/expert_indicators.svg.
1705 gboolean colors_ok = FALSE;
1706 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1707 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1708 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1709 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1710 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1711 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1713 gchar *expert_color_chat_str;
1714 gchar *expert_color_note_str;
1715 gchar *expert_color_warn_str;
1716 gchar *expert_color_error_str;
1717 gchar *expert_color_foreground_str;
1719 void proto_draw_colors_init(void)
1725 get_color(&expert_color_chat);
1726 get_color(&expert_color_note);
1727 get_color(&expert_color_warn);
1728 get_color(&expert_color_error);
1729 get_color(&expert_color_foreground);
1730 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1731 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1732 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1733 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1734 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1736 get_color(&hidden_proto_item);
1743 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1744 GtkTreeModel *tree_model, GtkTreeIter *iter,
1749 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1752 proto_draw_colors_init();
1755 /* for the various possible attributes, see:
1756 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1758 * color definitions can be found at:
1759 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1760 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1763 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1764 * weight/style: doesn't take any effect
1767 /* for each field, we have to reset the renderer attributes */
1768 g_object_set (cell, "foreground-set", FALSE, NULL);
1770 g_object_set (cell, "background-set", FALSE, NULL);
1772 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1773 g_object_set (cell, "underline-set", FALSE, NULL);
1775 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1776 g_object_set (cell, "style-set", FALSE, NULL);*/
1778 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1779 g_object_set (cell, "weight-set", FALSE, NULL);*/
1781 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1782 /* we use "[...]" to mark generated items, no need to change things here */
1784 /* as some fonts don't support italic, don't use this */
1785 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1786 g_object_set (cell, "style-set", TRUE, NULL);
1788 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1789 g_object_set (cell, "weight-set", TRUE, NULL);*/
1792 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1793 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1794 g_object_set (cell, "foreground-set", TRUE, NULL);
1797 if (fi && fi->hfinfo) {
1798 if(fi->hfinfo->type == FT_PROTOCOL) {
1799 g_object_set (cell, "background", "gray90", NULL);
1800 g_object_set (cell, "background-set", TRUE, NULL);
1801 g_object_set (cell, "foreground", "black", NULL);
1802 g_object_set (cell, "foreground-set", TRUE, NULL);
1803 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1804 g_object_set (cell, "weight-set", TRUE, NULL);*/
1807 if((fi->hfinfo->type == FT_FRAMENUM) ||
1808 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1809 render_as_url(cell);
1813 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1814 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1816 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1817 g_object_set (cell, "background-set", TRUE, NULL);
1820 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1821 g_object_set (cell, "background-set", TRUE, NULL);
1824 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1825 g_object_set (cell, "background-set", TRUE, NULL);
1828 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1829 g_object_set (cell, "background-set", TRUE, NULL);
1832 g_assert_not_reached();
1834 g_object_set (cell, "foreground", "black", NULL);
1835 g_object_set (cell, "foreground-set", TRUE, NULL);
1840 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1842 GtkWidget *tv_scrollw, *tree_view;
1843 GtkTreeStore *store;
1844 GtkCellRenderer *renderer;
1845 GtkTreeViewColumn *column;
1849 tv_scrollw = scrolled_window_new(NULL, NULL);
1850 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1853 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1854 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1855 g_object_unref(G_OBJECT(store));
1856 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1857 renderer = gtk_cell_renderer_text_new();
1858 g_object_set (renderer, "ypad", 0, NULL);
1859 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1860 -1, "Name", renderer,
1862 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1864 gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1867 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1868 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1869 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1870 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1871 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1872 set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
1873 gtk_widget_modify_font(tree_view, user_font_get_regular());
1874 remember_ptree_widget(tree_view);
1876 *tree_view_p = tree_view;
1882 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1885 for(i=0; i < num_tree_types; i++) {
1886 tree_is_expanded[i] = TRUE;
1888 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1892 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1895 for(i=0; i < num_tree_types; i++) {
1896 tree_is_expanded[i] = FALSE;
1898 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1902 struct proto_tree_draw_info {
1903 GtkTreeView *tree_view;
1908 main_proto_tree_draw(proto_tree *protocol_tree)
1910 proto_tree_draw(protocol_tree, tree_view_gbl);
1915 tree_view_follow_link(field_info *fi)
1919 if(fi->hfinfo->type == FT_FRAMENUM) {
1920 cf_goto_frame(&cfile, fi->value.value.uinteger);
1922 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1923 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1924 browser_open_url(url);
1930 /* If the user selected a position in the tree view, try to find
1931 * the item in the GUI proto_tree that corresponds to that byte, and
1934 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1936 GtkTreeSelection *sel;
1939 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1940 (gint) (((GdkEventButton *)event)->x),
1941 (gint) (((GdkEventButton *)event)->y),
1942 &path, NULL, NULL, NULL))
1944 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1946 /* if that's a doubleclick, try to follow the link */
1947 if(event->type == GDK_2BUTTON_PRESS) {
1948 GtkTreeModel *model;
1952 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1953 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1954 tree_view_follow_link(fi);
1957 else if (((GdkEventButton *)event)->button != 1) {
1958 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1959 gtk_tree_selection_select_path(sel, path);
1967 /* fill the whole protocol tree with the string values */
1969 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1971 GtkTreeStore *store;
1972 struct proto_tree_draw_info info;
1974 info.tree_view = GTK_TREE_VIEW(tree_view);
1977 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1980 * Clear out any crud left over in the display of the protocol
1981 * tree, by removing all nodes from the tree.
1982 * This is how it's done in testgtk.c in GTK+.
1984 gtk_tree_store_clear(store);
1986 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1990 /* fill a single protocol tree item with the string value */
1992 proto_tree_draw_node(proto_node *node, gpointer data)
1994 struct proto_tree_draw_info info;
1995 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1997 field_info *fi = PNODE_FINFO(node);
1998 gchar label_str[ITEM_LABEL_LENGTH];
2000 gboolean is_leaf, is_expanded;
2001 GtkTreeStore *store;
2005 g_assert(fi && "dissection with an invisible proto tree?");
2007 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2010 /* was a free format label produced? */
2012 label_ptr = fi->rep->representation;
2014 else { /* no, make a generic label */
2015 label_ptr = label_str;
2016 proto_item_fill_label(fi, label_str);
2019 if (node->first_child != NULL) {
2021 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2022 if (tree_is_expanded[fi->tree_type]) {
2026 is_expanded = FALSE;
2031 is_expanded = FALSE;
2034 if (PROTO_ITEM_IS_GENERATED(node)) {
2035 if (PROTO_ITEM_IS_HIDDEN(node)) {
2036 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2038 label_ptr = g_strdup_printf("[%s]", label_ptr);
2040 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2041 label_ptr = g_strdup_printf("<%s>", label_ptr);
2044 info.tree_view = parent_info->tree_view;
2045 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2046 gtk_tree_store_append(store, &iter, parent_info->iter);
2047 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2049 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2055 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2056 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2058 gtk_tree_view_expand_to_path(info.tree_view, path);
2060 gtk_tree_view_collapse_row(info.tree_view, path);
2061 gtk_tree_path_free(path);
2066 * Clear the hex dump and protocol tree panes.
2069 clear_tree_and_hex_views(void)
2071 /* Clear the hex dump by getting rid of all the byte views. */
2072 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2073 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2075 /* Add a placeholder byte view so that there's at least something
2076 displayed in the byte view notebook. */
2077 add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2079 /* Clear the protocol tree by removing all nodes in the ctree.
2080 This is how it's done in testgtk.c in GTK+ */
2081 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2085 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2087 if (recent.gui_bytes_view != view) {
2088 recent.gui_bytes_view = view;
2089 redraw_packet_bytes_all();