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 line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1157 if (start_line_pos == line_pos_end) 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(line_pos_end)-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(line_pos_end));
1167 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1173 } else if (mask_le) { /* LSB of mask first (little-endian) */
1174 while (start_line <= stop_line) {
1175 int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
1176 int line_pos = start_line_pos;
1178 while (line_pos < line_pos_end) {
1179 int lop = 8 / bits_per_one;
1180 int mask_per_one = (1 << bits_per_one) - 1;
1184 if ((mask & mask_per_one)) {
1186 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
1187 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
1188 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1192 mask >>= bits_per_one;
1195 /* at least one bit of ascii was one -> turn ascii on */
1198 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
1199 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
1200 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1212 } else { /* mask starting from end (big-endian) */
1213 while (start_line <= stop_line) {
1214 int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
1215 int line_pos = stop_line_pos-1;
1217 while (line_pos >= line_pos_start) {
1218 int lop = 8 / bits_per_one;
1219 int mask_per_one = (1 << bits_per_one) - 1;
1223 if ((mask & mask_per_one)) {
1225 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
1226 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
1227 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1231 mask >>= bits_per_one;
1234 /* at least one bit of ascii was one -> turn ascii on */
1237 gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
1238 gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
1239 gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
1248 stop_line_pos = per_line;
1253 return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
1258 /* Update the progress bar this many times when reading a file. */
1259 #define N_PROGBAR_UPDATES 100
1260 /* The minimum packet length required to check if a progres bar is needed or not */
1261 #define MIN_PACKET_LENGTH 1024
1264 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1265 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1266 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1267 * presumably why there's a progress bar for it.)
1269 * Perhaps what's needed is a custom widget (either one that lets you stuff
1270 * text into it more quickly, or one that's a "virtual" widget so that the
1271 * text for a row is constructed, via a callback, when the row is to be
1272 * displayed). A custom widget might also let us treat the offset, hex
1273 * data, and ASCII data as three columns, so you can select purely in
1274 * the hex dump column.
1277 packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
1279 int i = 0, j, k = 0, b, cur;
1280 guchar line[MAX_LINES_LEN + 1];
1281 static guchar hexchars[16] = {
1282 '0', '1', '2', '3', '4', '5', '6', '7',
1283 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1284 static const guint8 bitmask[8] = {
1285 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1287 unsigned int use_digits;
1290 progdlg_t *progbar = NULL;
1292 gboolean progbar_stop_flag;
1293 GTimeVal progbar_start_time;
1294 gchar progbar_status_str[100];
1295 int progbar_nextstep;
1296 int progbar_quantum;
1298 gtk_text_buffer_set_text(buf, "", 0);
1299 gtk_text_buffer_get_start_iter(buf, &iter);
1302 * How many of the leading digits of the offset will we supply?
1303 * We always supply at least 4 digits, but if the maximum offset
1304 * won't fit in 4 digits, we use as many digits as will be needed.
1306 if (((len - 1) & 0xF0000000) != 0)
1307 use_digits = 8; /* need all 8 digits */
1308 else if (((len - 1) & 0x0F000000) != 0)
1309 use_digits = 7; /* need 7 digits */
1310 else if (((len - 1) & 0x00F00000) != 0)
1311 use_digits = 6; /* need 6 digits */
1312 else if (((len - 1) & 0x000F0000) != 0)
1313 use_digits = 5; /* need 5 digits */
1315 use_digits = 4; /* we'll supply 4 digits */
1317 /* Record the number of digits in this text view. */
1318 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1320 /* Update the progress bar when it gets to this value. */
1321 if (len > MIN_PACKET_LENGTH){
1322 progbar_nextstep = 0;
1324 /* If length =< MIN_PACKET_LENGTH
1325 * there is no need to calculate the progress
1327 progbar_nextstep = len+1;
1330 /* When we reach the value that triggers a progress bar update,
1331 bump that value by this amount. */
1332 progbar_quantum = len/N_PROGBAR_UPDATES;
1333 /* Progress so far. */
1336 progbar_stop_flag = FALSE;
1337 g_get_current_time(&progbar_start_time);
1341 /* Create the progress bar if necessary.
1342 We check on every iteration of the loop, so that it takes no
1343 longer than the standard time to create it (otherwise, for a
1344 large packet, we might take considerably longer than that standard
1345 time in order to get to the next progress bar step). */
1346 if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1347 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1350 &progbar_start_time,
1353 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1354 when we update it, we have to run the GTK+ main loop to get it
1355 to repaint what's pending, and doing so may involve an "ioctl()"
1356 to see if there's any pending input from an X server, and doing
1357 that for every packet can be costly, especially on a big file. */
1358 if (i >= progbar_nextstep) {
1360 if (progbar != NULL) {
1361 /* let's not divide by zero. I should never be started
1362 * with count == 0, so let's assert that
1365 progbar_val = (gfloat) i / len;
1366 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1367 "%4u of %u bytes", i, len);
1368 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1371 progbar_nextstep += progbar_quantum;
1374 if (progbar_stop_flag) {
1375 /* Well, the user decided to abort the operation. Just stop,
1376 and arrange to return TRUE to our caller, so they know it
1377 was stopped explicitly. */
1381 /* Print the line number */
1385 c = (i >> (j*4)) & 0xF;
1386 line[cur++] = hexchars[c];
1392 switch (recent.gui_bytes_view) {
1394 k = i + BYTES_PER_LINE;
1397 k = i + BITS_PER_LINE;
1400 g_assert_not_reached();
1402 /* Print the hex bit */
1405 switch (recent.gui_bytes_view) {
1407 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1408 line[cur++] = hexchars[pd[i] & 0x0f];
1411 for (b = 0; b < 8; b++) {
1412 line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1416 g_assert_not_reached();
1419 switch (recent.gui_bytes_view) {
1421 line[cur++] = ' '; line[cur++] = ' ';
1424 for (b = 0; b < 8; b++) {
1429 g_assert_not_reached();
1433 /* Inter byte space if not at end of line */
1436 /* insert a space every BYTE_VIEW_SEP bytes */
1437 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1443 /* Print some space at the end of the line */
1444 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1446 /* Print the ASCII bit */
1451 if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
1454 else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
1455 c = EBCDIC_to_ASCII1(pd[i]);
1458 g_assert_not_reached();
1460 line[cur++] = isprint(c) ? c : '.';
1466 /* insert a space every BYTE_VIEW_SEP bytes */
1467 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1473 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1474 gtk_text_buffer_insert(buf, &iter, line, cur);
1479 /* We're done printing the packets; destroy the progress bar if
1481 if (progbar != NULL)
1482 destroy_progress_dlg(progbar);
1485 gtk_text_buffer_insert(buf, &iter, line, cur);
1490 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1491 int bend, guint32 bmask, int bmask_le,
1492 int astart, int aend, int encoding)
1494 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1495 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1499 GtkTextIter start, end;
1504 /* XXX: Apparently not a good idea; If a progress_bar
1505 * is displayed below in delayed_create_progress_dlg()
1506 * there will then be a crash internally in the gtk library.
1507 * (It appears that gtk_text_view_set_buffer
1508 * queues a callback to be run when this
1509 * thread is next idle. Unfortunately the call to
1510 * gtk_main_iteration() in delayed_create_progress_dlg()
1511 * causes the internal callback to be run which then
1512 * crashes (because the textview has no buffer ?))
1514 gtk_text_view_set_buffer(bv_text_view, NULL);
1517 /* attach a dummy buffer in place of the real buffer.
1518 * (XXX: Presumably this is done so there's no attempt
1519 * to display the real buffer until it has been
1520 * completely generated).
1522 gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
1524 packet_hex_print_common(buf, bv, pd, len, encoding);
1526 /* mark everything with "plain" tag */
1527 gtk_text_buffer_get_start_iter(buf, &start);
1528 gtk_text_buffer_get_end_iter(buf, &end);
1529 gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
1531 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
1533 /* mark reverse tags */
1534 mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
1535 packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
1537 gtk_text_view_set_buffer(bv_text_view, buf);
1539 /* scroll text into position */
1541 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1542 gtk_text_buffer_delete_mark(buf, mark);
1544 g_object_unref(buf);
1548 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1549 field_info *finfo, guint len)
1551 /* do the initial printing and save the information needed */
1552 /* to redraw the display if preferences change. */
1554 int bstart = -1, bend = -1, blen = -1;
1555 guint32 bmask = 0x00; int bmask_le = 0;
1556 int astart = -1, aend = -1, alen = -1;
1558 if (finfo != NULL) {
1559 bstart = finfo->start;
1560 blen = finfo->length;
1561 /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
1562 bmask = finfo->hfinfo->bitmask;
1563 astart = finfo->appendix_start;
1564 alen = finfo->appendix_length;
1566 if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
1568 else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
1570 else { /* unknown endianess - disable mask
1571 bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
1576 if (bmask == 0x00) {
1577 int bito = FI_GET_BITS_OFFSET(finfo);
1578 int bitc = FI_GET_BITS_SIZE(finfo);
1579 int bitt = bito + bitc;
1581 /* construct mask using bito & bitc */
1582 /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
1583 if (bitt > 0 && bitt < 32) {
1585 bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
1586 bmask_le = 0; /* ? */
1591 if (bstart >= 0 && blen > 0) {
1592 bend = bstart + blen;
1594 if (astart >= 0 && alen > 0) {
1595 aend = astart + alen;
1598 if (bend == -1 && aend != -1) {
1605 /* save the information needed to redraw the text */
1606 /* should we save the fd & finfo pointers instead ?? */
1607 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1608 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1609 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1610 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1611 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1612 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1613 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1614 GUINT_TO_POINTER((guint)fd->flags.encoding));
1616 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1620 * Redraw the text using the saved information; usually called if
1621 * the preferences have changed.
1624 packet_hex_reprint(GtkWidget *bv)
1626 int start, end, mask, mask_le, encoding;
1631 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1632 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1633 mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1634 mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1635 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1636 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1637 data = get_byte_view_data_and_length(bv, &len);
1638 g_assert(data != NULL);
1639 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1641 packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1644 /* List of all protocol tree widgets, so we can globally set the selection
1645 mode and font of all of them. */
1646 static GList *ptree_widgets;
1648 /* Add a protocol tree widget to the list of protocol tree widgets. */
1649 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1652 remember_ptree_widget(GtkWidget *ptreew)
1654 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1656 /* Catch the "destroy" event on the widget, so that we remove it from
1657 the list when it's destroyed. */
1658 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1661 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1663 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1665 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1668 /* Set the selection mode of a given packet tree window. */
1670 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1672 GtkTreeSelection *selection;
1674 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1675 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1676 I think "browse" in Wireshark makes more sense than "SINGLE" in
1679 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1682 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1687 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1689 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1692 /* Set the selection mode of all packet tree windows. */
1694 set_ptree_sel_browse_all(gboolean val)
1696 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1700 set_ptree_font_cb(gpointer data, gpointer user_data)
1702 gtk_widget_modify_font((GtkWidget *)data,
1703 (PangoFontDescription *)user_data);
1707 set_ptree_font_all(PangoFontDescription *font)
1709 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1714 * Each expert_color_* level below should match the light gradient
1715 * colors in image/expert_indicators.svg.
1717 gboolean colors_ok = FALSE;
1718 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1719 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1720 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1721 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1722 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1723 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1725 gchar *expert_color_chat_str;
1726 gchar *expert_color_note_str;
1727 gchar *expert_color_warn_str;
1728 gchar *expert_color_error_str;
1729 gchar *expert_color_foreground_str;
1731 void proto_draw_colors_init(void)
1737 get_color(&expert_color_chat);
1738 get_color(&expert_color_note);
1739 get_color(&expert_color_warn);
1740 get_color(&expert_color_error);
1741 get_color(&expert_color_foreground);
1742 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1743 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1744 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1745 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1746 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1748 get_color(&hidden_proto_item);
1755 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1756 GtkTreeModel *tree_model, GtkTreeIter *iter,
1761 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1764 proto_draw_colors_init();
1767 /* for the various possible attributes, see:
1768 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1770 * color definitions can be found at:
1771 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1772 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1775 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1776 * weight/style: doesn't take any effect
1779 /* for each field, we have to reset the renderer attributes */
1780 g_object_set (cell, "foreground-set", FALSE, NULL);
1782 g_object_set (cell, "background-set", FALSE, NULL);
1784 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1785 g_object_set (cell, "underline-set", FALSE, NULL);
1787 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1788 g_object_set (cell, "style-set", FALSE, NULL);*/
1790 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1791 g_object_set (cell, "weight-set", FALSE, NULL);*/
1793 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1794 /* we use "[...]" to mark generated items, no need to change things here */
1796 /* as some fonts don't support italic, don't use this */
1797 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1798 g_object_set (cell, "style-set", TRUE, NULL);
1800 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1801 g_object_set (cell, "weight-set", TRUE, NULL);*/
1804 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1805 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1806 g_object_set (cell, "foreground-set", TRUE, NULL);
1809 if (fi && fi->hfinfo) {
1810 if(fi->hfinfo->type == FT_PROTOCOL) {
1811 g_object_set (cell, "background", "gray90", NULL);
1812 g_object_set (cell, "background-set", TRUE, NULL);
1813 g_object_set (cell, "foreground", "black", NULL);
1814 g_object_set (cell, "foreground-set", TRUE, NULL);
1815 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1816 g_object_set (cell, "weight-set", TRUE, NULL);*/
1819 if((fi->hfinfo->type == FT_FRAMENUM) ||
1820 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1821 render_as_url(cell);
1825 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1826 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1828 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1829 g_object_set (cell, "background-set", TRUE, NULL);
1832 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1833 g_object_set (cell, "background-set", TRUE, NULL);
1836 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1837 g_object_set (cell, "background-set", TRUE, NULL);
1840 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1841 g_object_set (cell, "background-set", TRUE, NULL);
1844 g_assert_not_reached();
1846 g_object_set (cell, "foreground", "black", NULL);
1847 g_object_set (cell, "foreground-set", TRUE, NULL);
1852 main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
1854 GtkWidget *tv_scrollw, *tree_view;
1855 GtkTreeStore *store;
1856 GtkCellRenderer *renderer;
1857 GtkTreeViewColumn *column;
1861 tv_scrollw = scrolled_window_new(NULL, NULL);
1862 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1865 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1866 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1867 g_object_unref(G_OBJECT(store));
1868 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1869 renderer = gtk_cell_renderer_text_new();
1870 g_object_set (renderer, "ypad", 0, NULL);
1871 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1872 -1, "Name", renderer,
1874 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1876 gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1879 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1880 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1881 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1882 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1883 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1884 set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
1885 gtk_widget_modify_font(tree_view, user_font_get_regular());
1886 remember_ptree_widget(tree_view);
1888 *tree_view_p = tree_view;
1894 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1897 for(i=0; i < num_tree_types; i++) {
1898 tree_is_expanded[i] = TRUE;
1900 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1904 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1907 for(i=0; i < num_tree_types; i++) {
1908 tree_is_expanded[i] = FALSE;
1910 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1914 struct proto_tree_draw_info {
1915 GtkTreeView *tree_view;
1920 main_proto_tree_draw(proto_tree *protocol_tree)
1922 proto_tree_draw(protocol_tree, tree_view_gbl);
1927 tree_view_follow_link(field_info *fi)
1931 if(fi->hfinfo->type == FT_FRAMENUM) {
1932 cf_goto_frame(&cfile, fi->value.value.uinteger);
1934 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1935 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1936 browser_open_url(url);
1942 /* If the user selected a position in the tree view, try to find
1943 * the item in the GUI proto_tree that corresponds to that byte, and
1946 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1948 GtkTreeSelection *sel;
1951 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1952 (gint) (((GdkEventButton *)event)->x),
1953 (gint) (((GdkEventButton *)event)->y),
1954 &path, NULL, NULL, NULL))
1956 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1958 /* if that's a doubleclick, try to follow the link */
1959 if(event->type == GDK_2BUTTON_PRESS) {
1960 GtkTreeModel *model;
1964 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1965 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1966 tree_view_follow_link(fi);
1969 else if (((GdkEventButton *)event)->button != 1) {
1970 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1971 gtk_tree_selection_select_path(sel, path);
1979 /* fill the whole protocol tree with the string values */
1981 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1983 GtkTreeStore *store;
1984 struct proto_tree_draw_info info;
1986 info.tree_view = GTK_TREE_VIEW(tree_view);
1989 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1992 * Clear out any crud left over in the display of the protocol
1993 * tree, by removing all nodes from the tree.
1994 * This is how it's done in testgtk.c in GTK+.
1996 gtk_tree_store_clear(store);
1998 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2002 /* fill a single protocol tree item with the string value */
2004 proto_tree_draw_node(proto_node *node, gpointer data)
2006 struct proto_tree_draw_info info;
2007 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2009 field_info *fi = PNODE_FINFO(node);
2010 gchar label_str[ITEM_LABEL_LENGTH];
2012 gboolean is_leaf, is_expanded;
2013 GtkTreeStore *store;
2017 g_assert(fi && "dissection with an invisible proto tree?");
2019 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
2022 /* was a free format label produced? */
2024 label_ptr = fi->rep->representation;
2026 else { /* no, make a generic label */
2027 label_ptr = label_str;
2028 proto_item_fill_label(fi, label_str);
2031 if (node->first_child != NULL) {
2033 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2034 if (tree_is_expanded[fi->tree_type]) {
2038 is_expanded = FALSE;
2043 is_expanded = FALSE;
2046 if (PROTO_ITEM_IS_GENERATED(node)) {
2047 if (PROTO_ITEM_IS_HIDDEN(node)) {
2048 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
2050 label_ptr = g_strdup_printf("[%s]", label_ptr);
2052 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
2053 label_ptr = g_strdup_printf("<%s>", label_ptr);
2056 info.tree_view = parent_info->tree_view;
2057 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2058 gtk_tree_store_append(store, &iter, parent_info->iter);
2059 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2061 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
2067 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2068 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2070 gtk_tree_view_expand_to_path(info.tree_view, path);
2072 gtk_tree_view_collapse_row(info.tree_view, path);
2073 gtk_tree_path_free(path);
2078 * Clear the hex dump and protocol tree panes.
2081 clear_tree_and_hex_views(void)
2083 /* Clear the hex dump by getting rid of all the byte views. */
2084 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
2085 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
2087 /* Add a placeholder byte view so that there's at least something
2088 displayed in the byte view notebook. */
2089 add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
2091 /* Clear the protocol tree by removing all nodes in the ctree.
2092 This is how it's done in testgtk.c in GTK+ */
2093 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
2097 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
2099 if (recent.gui_bytes_view != view) {
2100 recent.gui_bytes_view = view;
2101 redraw_packet_bytes_all();