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>
52 #include <epan/packet.h>
53 #include <epan/charsets.h>
57 #include <epan/prefs.h>
59 #include "capture_file_dlg.h"
60 #include "proto_draw.h"
61 #include "packet_win.h"
63 #include "dlg_utils.h"
66 #include "gui_utils.h"
67 #include "gtkglobals.h"
68 #include "compat_macros.h"
69 #include "alert_box.h"
70 #include "simple_dialog.h"
71 #include "progress_dlg.h"
72 #include "font_utils.h"
74 #include "../ui_util.h"
75 #include "file_util.h"
76 #include "webbrowser.h"
78 #if GTK_MAJOR_VERSION >= 2 && _WIN32
79 #include <gdk/gdkwin32.h>
81 #include "win32-file-dlg.h"
84 #define BYTE_VIEW_WIDTH 16
85 #define BYTE_VIEW_SEP 8
87 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
88 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
89 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
90 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
91 #define E_BYTE_VIEW_START_KEY "byte_view_start"
92 #define E_BYTE_VIEW_END_KEY "byte_view_end"
93 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
94 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
95 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
98 #if GTK_MAJOR_VERSION < 2
99 GtkStyle *item_style = NULL;
102 /* gtk_tree_view_expand_to_path doesn't exist in gtk+ v2.0 so we must include it
103 * when building with this version (taken from gtk+ v2.2.4) */
104 #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION == 0
106 * gtk_tree_view_expand_to_path:
107 * @tree_view: A #GtkTreeView.
108 * @path: path to a row.
110 * Expands the row at @path. This will also expand all parent rows of
111 * @path as necessary.
116 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
123 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
124 g_return_if_fail (path != NULL);
126 depth = gtk_tree_path_get_depth (path);
127 indices = gtk_tree_path_get_indices (path);
129 tmp = gtk_tree_path_new ();
130 g_return_if_fail (tmp != NULL);
132 for (i = 0; i < depth; i++)
134 gtk_tree_path_append_index (tmp, indices[i]);
135 gtk_tree_view_expand_row (tree_view, tmp, FALSE);
138 gtk_tree_path_free (tmp);
143 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
144 proto_tree *tree, GtkWidget *tree_view);
147 proto_tree_draw_node(proto_node *node, gpointer data);
149 /* Get the current text window for the notebook. */
151 get_notebook_bv_ptr(GtkWidget *nb_ptr)
156 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
157 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
159 return GTK_BIN(bv_page)->child;
165 * Get the data and length for a byte view, given the byte view page.
166 * Return the pointer, or NULL on error, and set "*data_len" to the length.
169 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
171 tvbuff_t *byte_view_tvb;
172 const guint8 *data_ptr;
174 byte_view_tvb = OBJECT_GET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY);
175 if (byte_view_tvb == NULL)
178 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
179 *data_len = tvb_length(byte_view_tvb);
184 * Set the current text window for the notebook to the window that
185 * refers to a particular tvbuff.
188 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
191 GtkWidget *bv_page, *bv;
195 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
197 bv = GTK_BIN(bv_page)->child;
198 bv_tvb = OBJECT_GET_DATA(bv, E_BYTE_VIEW_TVBUFF_KEY);
201 gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
207 /* Redraw a given byte view window. */
209 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
215 bv = get_notebook_bv_ptr(nb);
217 data = get_byte_view_data_and_length(bv, &len);
219 packet_hex_print(bv, data, fd, finfo, len);
223 /* Redraw all byte view windows. */
225 redraw_hex_dump_all(void)
227 if (cfile.current_frame != NULL)
228 redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
230 redraw_hex_dump_packet_wins();
232 #if GTK_MAJOR_VERSION >= 2
233 /* XXX - this is a hack, to workaround a bug in GTK2.x!
234 when changing the font size, even refilling of the corresponding
235 gtk_text_buffer doesn't seem to trigger an update.
236 The only workaround is to freshly select the frame, which will remove any
237 existing notebook tabs and "restart" the whole byte view again. */
238 if (cfile.current_frame != NULL)
239 cf_goto_frame(&cfile, cfile.current_frame->num);
243 #if GTK_MAJOR_VERSION < 2
245 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
248 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
249 GtkTreePath *path _U_, gpointer user_data _U_)
253 #if GTK_MAJOR_VERSION >= 2
256 model = gtk_tree_view_get_model(tree_view);
257 gtk_tree_model_get(model, iter, 1, &finfo, -1);
259 finfo = gtk_ctree_node_get_row_data( ctree, node);
264 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
265 * are thus presumably leaf nodes and cannot be expanded.
267 if (finfo->tree_type != -1) {
268 g_assert(finfo->tree_type >= 0 &&
269 finfo->tree_type < num_tree_types);
270 tree_is_expanded[finfo->tree_type] = TRUE;
274 #if GTK_MAJOR_VERSION < 2
276 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
279 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
280 GtkTreePath *path _U_, gpointer user_data _U_)
284 #if GTK_MAJOR_VERSION >= 2
287 model = gtk_tree_view_get_model(tree_view);
288 gtk_tree_model_get(model, iter, 1, &finfo, -1);
290 finfo = gtk_ctree_node_get_row_data( ctree, node);
295 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
296 * are thus presumably leaf nodes and cannot be collapsed.
298 if (finfo->tree_type != -1) {
299 g_assert(finfo->tree_type >= 0 &&
300 finfo->tree_type < num_tree_types);
301 tree_is_expanded[finfo->tree_type] = FALSE;
305 #if GTK_MAJOR_VERSION < 2
307 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
309 if (event->keyval != GDK_Return)
311 gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
315 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
316 #define BYTES_PER_LINE 16 /* max byte values in a line */
317 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
318 /* max number of characters hex dump takes -
319 2 digits plus trailing blank
320 plus separator between first and
322 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
323 /* number of characters those bytes take;
324 3 characters per byte of hex dump,
325 2 blanks separating hex from ASCII,
326 1 character per byte of ASCII dump */
327 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
328 /* number of characters per line;
329 offset, 2 blanks separating offset
330 from data dump, data dump */
332 /* Which byte the offset is referring to. Associates
333 * whitespace with the preceding digits. */
335 byte_num(int offset, int start_point)
337 return (offset - start_point) / 3;
340 #if GTK_MAJOR_VERSION >= 2
341 struct field_lookup_info {
347 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
351 struct field_lookup_info *fli = (struct field_lookup_info *)data;
353 gtk_tree_model_get(model, iter, 1, &fi, -1);
361 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
363 struct field_lookup_info fli;
365 g_assert(finfo != NULL);
367 model = gtk_tree_view_get_model(tree_view);
369 gtk_tree_model_foreach(model, lookup_finfo, &fli);
371 return gtk_tree_model_get_path(model, &fli.iter);
376 /* If the user selected a certain byte in the byte view, try to find
377 * the item in the GUI proto_tree that corresponds to that byte, and:
379 * if we succeed, select it, and return TRUE;
380 * if we fail, return FALSE. */
382 byte_view_select(GtkWidget *widget, GdkEventButton *event)
385 #if GTK_MAJOR_VERSION < 2
387 /*GtkCTreeNode *node, *parent;*/
388 GtkText *bv = GTK_TEXT(widget);
390 GtkTreeView *tree_view;
391 GtkTextView *bv = GTK_TEXT_VIEW(widget);
409 * Get the number of digits of offset being displayed, and
410 * compute the columns of various parts of the display.
412 ndigits = GPOINTER_TO_UINT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY));
415 * The column of the first hex digit in the first half.
416 * That starts after "ndigits" digits of offset and two
419 digits_start_1 = ndigits + 2;
422 * The column of the last hex digit in the first half.
423 * There are BYTES_PER_LINE/2 bytes displayed in the first
424 * half; there are 2 characters per byte, plus a separating
425 * blank after all but the last byte's characters.
427 * Then subtract 1 to get the last column of the first half
428 * rather than the first column after the first half.
430 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
431 (BYTES_PER_LINE/2 - 1) - 1;
434 * The column of the first hex digit in the second half.
435 * Add back the 1 to get the first column after the first
436 * half, and then add 2 for the 2 separating blanks between
439 digits_start_2 = digits_end_1 + 3;
442 * The column of the last hex digit in the second half.
443 * Add the same value we used to get "digits_end_1" from
446 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
447 (BYTES_PER_LINE/2 - 1) - 1;
450 * The column of the first "text dump" character in the first half.
451 * Add back the 1 to get the first column after the second
452 * half's hex dump, and then add 3 for the 3 separating blanks
453 * between the hex and text dummp.
455 text_start_1 = digits_end_2 + 4;
458 * The column of the last "text dump" character in the first half.
459 * There are BYTES_PER_LINE/2 bytes displayed in the first
460 * half; there is 1 character per byte.
462 * Then subtract 1 to get the last column of the first half
463 * rather than the first column after the first half.
465 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
468 * The column of the first "text dump" character in the second half.
469 * Add back the 1 to get the first column after the first half,
470 * and then add 1 for the separating blank between the halves.
472 text_start_2 = text_end_1 + 2;
475 * The column of the last "text dump" character in second half.
476 * Add the same value we used to get "text_end_1" from
479 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
481 tree = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_PTR);
484 * Somebody clicked on the dummy byte view; do nothing.
488 #if GTK_MAJOR_VERSION < 2
489 ctree = GTK_CTREE(OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_VIEW_PTR));
491 tree_view = GTK_TREE_VIEW(OBJECT_GET_DATA(widget,
492 E_BYTE_VIEW_TREE_VIEW_PTR));
495 #if GTK_MAJOR_VERSION < 2
496 /* Given the mouse (x,y) and the current GtkText (h,v)
497 * adjustments, and the size of the font, figure out
498 * which text column/row the user selected. This could be off
499 * if the bold version of the font is bigger than the
500 * regular version of the font. */
501 column = (int) ((bv->hadj->value + event->x) / user_font_get_regular_width());
502 row = (int) ((bv->vadj->value + event->y) / user_font_get_regular_height());
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 /* Given the column and row, determine which byte offset
514 * the user clicked on. */
515 if (column >= digits_start_1 && column <= digits_end_1) {
516 byte = byte_num(column, digits_start_1);
521 else if (column >= digits_start_2 && column <= digits_end_2) {
522 byte = byte_num(column, digits_start_2);
528 else if (column >= text_start_1 && column <= text_end_1) {
529 byte = column - text_start_1;
531 else if (column >= text_start_2 && column <= text_end_2) {
532 byte = 8 + column - text_start_2;
535 /* The user didn't select a hex digit or
536 * text-dump character. */
540 /* Add the number of bytes from the previous rows. */
543 /* Get the data source tvbuff */
544 tvb = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TVBUFF_KEY);
546 #if GTK_MAJOR_VERSION < 2
547 return highlight_field(tvb, byte, ctree, tree);
549 return highlight_field(tvb, byte, tree_view, tree);
553 /* This highlights the field in the proto tree that is at position byte */
554 #if GTK_MAJOR_VERSION < 2
556 highlight_field(tvbuff_t *tvb, gint byte, GtkCTree *ctree,
560 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
564 #if GTK_MAJOR_VERSION < 2
565 GtkCTreeNode *node, *parent;
568 GtkTreePath *first_path, *path;
570 struct field_lookup_info fli;
574 /* Find the finfo that corresponds to our byte. */
575 finfo = proto_find_field_from_offset(tree, byte, tvb);
581 #if GTK_MAJOR_VERSION < 2
582 node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
585 /* Expand and select our field's row */
586 gtk_ctree_expand(ctree, node);
587 gtk_ctree_select(ctree, node);
588 expand_tree(ctree, node, NULL);
590 /* ... and its parents */
591 parent = GTK_CTREE_ROW(node)->parent;
593 gtk_ctree_expand(ctree, parent);
594 expand_tree(ctree, parent, NULL);
595 parent = GTK_CTREE_ROW(parent)->parent;
598 /* And position the window so the selection is visible.
599 * Position the selection in the middle of the viewable
601 gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
605 model = gtk_tree_view_get_model(tree_view);
607 gtk_tree_model_foreach(model, lookup_finfo, &fli);
609 /* Expand our field's row */
610 first_path = gtk_tree_model_get_path(model, &fli.iter);
611 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
612 expand_tree(tree_view, &fli.iter, NULL, NULL);
614 /* ... and its parents */
615 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
616 path = gtk_tree_model_get_path(model, &parent);
617 gtk_tree_view_expand_row(tree_view, path, FALSE);
618 expand_tree(tree_view, &parent, NULL, NULL);
620 gtk_tree_path_free(path);
623 /* select our field's row */
624 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
627 /* And position the window so the selection is visible.
628 * Position the selection in the middle of the viewable
630 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
632 gtk_tree_path_free(first_path);
638 /* Calls functions for different mouse-button presses. */
640 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
642 GdkEventButton *event_button = NULL;
644 if(widget == NULL || event == NULL || data == NULL) {
648 if(event->type == GDK_BUTTON_PRESS) {
649 event_button = (GdkEventButton *) event;
651 /* To qoute the "Gdk Event Structures" doc:
652 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
653 switch(event_button->button) {
656 return byte_view_select(widget, event_button);
658 return popup_menu_handler(widget, event, data);
672 byte_nb = gtk_notebook_new();
673 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
675 /* this will only have an effect, if no tabs are shown */
676 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
678 /* set the tabs scrollable, if they don't fit into the pane */
679 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
681 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
682 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
684 /* Add a placeholder byte view so that there's at least something
685 displayed in the byte view notebook. */
686 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
692 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
694 const guint8 *byte_data;
697 byte_data = get_byte_view_data_and_length(bv, &byte_len);
698 if (byte_data == NULL) {
699 /* This must be the dummy byte view if no packet is selected. */
702 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
706 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
707 proto_tree *tree, GtkWidget *tree_view)
709 GtkWidget *byte_view, *byte_scrollw, *label;
710 #if GTK_MAJOR_VERSION >= 2
715 /* Byte view. Create a scrolled window for the text. */
716 byte_scrollw = scrolled_window_new(NULL, NULL);
717 #if GTK_MAJOR_VERSION < 2
718 /* The horizontal scrollbar of the scroll-window doesn't seem
719 * to affect the GtkText widget at all, even when line wrapping
720 * is turned off in the GtkText widget and there is indeed more
721 * horizontal data. */
722 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
723 /* Horizontal */GTK_POLICY_NEVER,
724 /* Vertical*/ GTK_POLICY_ALWAYS);
726 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
729 /* Add scrolled pane to tabbed window */
730 label = gtk_label_new(name);
731 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
733 gtk_widget_show(byte_scrollw);
735 #if GTK_MAJOR_VERSION < 2
736 byte_view = gtk_text_new(NULL, NULL);
737 gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
738 gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
739 gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
741 byte_view = gtk_text_view_new();
742 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
743 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
744 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
745 style = gtk_widget_get_style(GTK_WIDGET(byte_view));
746 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
747 gtk_text_buffer_create_tag(buf, "reverse",
748 "font-desc", user_font_get_regular(),
749 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
750 "background-gdk", &style->base[GTK_STATE_SELECTED],
752 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
754 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, tvb);
755 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
757 SIGNAL_CONNECT(byte_view, "show", byte_view_realize_cb, NULL);
758 SIGNAL_CONNECT(byte_view, "button_press_event", byte_view_button_press_cb,
759 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
761 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, tree);
762 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
764 gtk_widget_show(byte_view);
766 /* no tabs if this is the first page */
767 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
768 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
770 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
772 /* set this page (this will print the packet data) */
773 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb),
774 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
780 add_main_byte_views(epan_dissect_t *edt)
782 add_byte_views(edt, tree_view, byte_nb_ptr);
786 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
787 GtkWidget *byte_nb_ptr)
793 * Get rid of all the old notebook tabs.
795 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
796 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
799 * Add to the specified byte view notebook tabs for hex dumps
800 * of all the data sources for the specified frame.
802 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
804 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
809 * Initially select the first byte view.
811 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
816 static GtkWidget *savehex_dlg=NULL;
819 savehex_dlg_destroy_cb(void)
825 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
827 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
829 gboolean end_of_line = TRUE; /* Initial state is end of line */
830 int byte_line_part_length;
835 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
836 hex_str = g_string_new("");
837 char_str= g_string_new("");
842 g_string_sprintfa(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
845 g_string_sprintfa(hex_str," %02x",*data_p);
847 g_string_sprintfa(char_str,"%c",isprint(*data_p) ? *data_p : '.');
852 /* Look ahead to see if this is the end of the data */
853 byte_line_part_length = (++i) % byte_line_length;
855 /* End of data - need to fill in spaces in hex string and then do "end of line".
858 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
859 g_string_append(hex_str," "); /* Three spaces for each missing byte */
863 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
869 g_string_append(copy_buffer, hex_str->str);
871 /* Two spaces between hex and text */
872 g_string_append_c(copy_buffer, ' ');
873 g_string_append_c(copy_buffer, ' ');
874 g_string_append(copy_buffer, char_str->str);
876 /* Setup ready for next line */
877 g_string_assign(char_str,"");
878 g_string_assign(hex_str, "\n");
882 g_string_free(hex_str, TRUE);
883 g_string_free(char_str, TRUE);
887 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
892 if(isprint(*data_p)) {
894 } else if(*data_p==0x0a) {
895 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
897 return 1; /* Just ignore non-printable bytes */
899 g_string_append_c(copy_buffer,to_append);
904 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
906 g_string_sprintfa(copy_buffer, "%02x", *data_p);
911 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
916 int bytes_consumed = 0;
919 const guint8* data_p;
921 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
923 bv = get_notebook_bv_ptr(byte_nb_ptr);
925 /* shouldn't happen */
926 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
930 data_p = get_byte_view_data_and_length(bv, &len);
931 g_assert(data_p != NULL);
933 flags = data_type & CD_FLAGSMASK;
934 data_type = data_type & CD_TYPEMASK;
936 if(flags & CD_FLAGS_SELECTEDONLY) {
939 /* Get the start and end of the highlighted bytes.
940 * XXX The keys appear to be REVERSED start <-> end throughout this file!
941 * Should this be fixed? There is one exception - packet_hex_reprint,
942 * so can't just change it round without functional change.
944 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
945 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
947 if(start >= 0 && end > start && (end - start <= (int)len)) {
955 /* This is too different from other text formats - handle separately */
956 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
959 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
960 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
962 #if (GTK_MAJOR_VERSION >= 2)
964 /* Completely different logic to text copies - leave copy buffer alone */
965 copy_binary_to_clipboard(data_p,len);
969 /* Incrementally write to text buffer in various formats */
973 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
976 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
979 g_assert_not_reached();
983 g_assert(bytes_consumed>0);
984 data_p += bytes_consumed;
985 len -= bytes_consumed;
990 if(copy_buffer->len > 0) {
991 copy_to_clipboard(copy_buffer);
994 g_string_free(copy_buffer, TRUE);
997 /* save the current highlighted hex data */
999 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
1004 const guint8 *data_p = NULL;
1005 const char *file = NULL;
1007 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1008 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
1010 file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(savehex_dlg));
1013 if (!file ||! *file) {
1014 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
1018 /* Must check if file name exists first */
1020 bv = get_notebook_bv_ptr(byte_nb_ptr);
1022 /* shouldn't happen */
1023 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1027 * Retrieve the info we need
1029 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1030 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1031 data_p = get_byte_view_data_and_length(bv, &len);
1033 if (data_p == NULL || start == -1 || start > end) {
1034 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1035 "No data selected to save!");
1039 fd = eth_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
1041 open_failure_alert_box(file, errno, TRUE);
1044 if (eth_write(fd, data_p + start, end - start) < 0) {
1045 write_failure_alert_box(file, errno);
1049 if (eth_close(fd) < 0) {
1050 write_failure_alert_box(file, errno);
1054 /* Get rid of the dialog box */
1055 window_destroy(GTK_WIDGET(savehex_dlg));
1058 /* Launch the dialog box to put up the file selection box etc */
1059 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1063 const guint8 *data_p = NULL;
1069 #if GTK_MAJOR_VERSION >= 2 && _WIN32
1070 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
1074 /* don't show up the dialog, if no data has to be saved */
1075 bv = get_notebook_bv_ptr(byte_nb_ptr);
1077 /* shouldn't happen */
1078 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1081 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1082 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1083 data_p = get_byte_view_data_and_length(bv, &len);
1085 if (data_p == NULL || start == -1 || start > end) {
1086 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1090 /* if the window is already open, bring it to front */
1092 reactivate_window(savehex_dlg);
1097 * Build the dialog box we need.
1099 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1102 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1103 end - start, plurality(end - start, "byte", "bytes"));
1104 dlg_lb = gtk_label_new(label);
1106 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1107 gtk_widget_show(dlg_lb);
1109 SIGNAL_CONNECT(savehex_dlg, "destroy", savehex_dlg_destroy_cb, NULL);
1111 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1112 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1113 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1115 window_destroy(savehex_dlg);
1118 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1119 pointer to the file selection box widget */
1120 SIGNAL_CONNECT(GTK_FILE_SELECTION (savehex_dlg)->ok_button, "clicked",
1121 savehex_save_clicked_cb, savehex_dlg);
1123 window_set_cancel_button(savehex_dlg,
1124 GTK_FILE_SELECTION(savehex_dlg)->cancel_button, window_cancel_button_cb);
1126 SIGNAL_CONNECT(savehex_dlg, "delete_event", window_delete_event_cb, NULL);
1128 gtk_file_selection_set_filename(GTK_FILE_SELECTION(savehex_dlg), "");
1130 gtk_widget_show_all(savehex_dlg);
1131 window_present(savehex_dlg);
1137 /* Update the progress bar this many times when reading a file. */
1138 #define N_PROGBAR_UPDATES 100
1142 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1143 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1144 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1145 * presumably why there's a progress bar for it.)
1147 * Perhaps what's needed is a custom widget (either one that lets you stuff
1148 * text into it more quickly, or one that's a "virtual" widget so that the
1149 * text for a row is constructed, via a callback, when the row is to be
1150 * displayed). A custom widget might also let us treat the offset, hex
1151 * data, and ASCII data as three columns, so you can select purely in
1152 * the hex dump column.
1155 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1156 int bend, int astart, int aend, int encoding)
1158 int i = 0, j, k, cur;
1159 guchar line[MAX_LINE_LEN + 1];
1160 static guchar hexchars[16] = {
1161 '0', '1', '2', '3', '4', '5', '6', '7',
1162 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1164 unsigned int use_digits;
1165 gboolean reverse, newreverse;
1166 #if GTK_MAJOR_VERSION < 2
1167 GdkFont *cur_font, *new_font;
1169 GtkText *bv_text = GTK_TEXT(bv);
1171 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1172 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1174 const char *revstyle;
1177 GtkTextMark *mark = NULL;
1180 progdlg_t *progbar = NULL;
1182 gboolean progbar_stop_flag;
1183 GTimeVal progbar_start_time;
1184 gchar progbar_status_str[100];
1185 int progbar_nextstep;
1186 int progbar_quantum;
1188 #if GTK_MAJOR_VERSION < 2
1189 /* Freeze the text for faster display */
1190 gtk_text_freeze(bv_text);
1192 /* Clear out the text */
1193 gtk_text_set_point(bv_text, 0);
1194 /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
1195 http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
1196 http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
1197 for more information */
1198 gtk_adjustment_set_value(bv_text->vadj, 0.0);
1199 gtk_text_forward_delete(bv_text, gtk_text_get_length(bv_text));
1201 gtk_text_buffer_set_text(buf, "", 0);
1202 gtk_text_buffer_get_start_iter(buf, &iter);
1206 * How many of the leading digits of the offset will we supply?
1207 * We always supply at least 4 digits, but if the maximum offset
1208 * won't fit in 4 digits, we use as many digits as will be needed.
1210 if (((len - 1) & 0xF0000000) != 0)
1211 use_digits = 8; /* need all 8 digits */
1212 else if (((len - 1) & 0x0F000000) != 0)
1213 use_digits = 7; /* need 7 digits */
1214 else if (((len - 1) & 0x00F00000) != 0)
1215 use_digits = 6; /* need 6 digits */
1216 else if (((len - 1) & 0x000F0000) != 0)
1217 use_digits = 5; /* need 5 digits */
1219 use_digits = 4; /* we'll supply 4 digits */
1221 /* Record the number of digits in this text view. */
1222 OBJECT_SET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1224 /* Update the progress bar when it gets to this value. */
1225 progbar_nextstep = 0;
1226 /* When we reach the value that triggers a progress bar update,
1227 bump that value by this amount. */
1228 progbar_quantum = len/N_PROGBAR_UPDATES;
1229 /* Progress so far. */
1232 progbar_stop_flag = FALSE;
1233 g_get_current_time(&progbar_start_time);
1236 /* Create the progress bar if necessary.
1237 We check on every iteration of the loop, so that it takes no
1238 longer than the standard time to create it (otherwise, for a
1239 large packet, we might take considerably longer than that standard
1240 time in order to get to the next progress bar step). */
1241 if (progbar == NULL)
1242 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1245 &progbar_start_time,
1248 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1249 when we update it, we have to run the GTK+ main loop to get it
1250 to repaint what's pending, and doing so may involve an "ioctl()"
1251 to see if there's any pending input from an X server, and doing
1252 that for every packet can be costly, especially on a big file. */
1253 if (i >= progbar_nextstep) {
1254 /* let's not divide by zero. I should never be started
1255 * with count == 0, so let's assert that
1258 progbar_val = (gfloat) i / len;
1260 if (progbar != NULL) {
1261 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1262 "%4u of %u bytes", i, len);
1263 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1266 progbar_nextstep += progbar_quantum;
1269 if (progbar_stop_flag) {
1270 /* Well, the user decided to abort the operation. Just stop,
1271 and arrange to return TRUE to our caller, so they know it
1272 was stopped explicitly. */
1276 /* Print the line number */
1281 c = (i >> (j*4)) & 0xF;
1282 line[cur++] = hexchars[c];
1288 /* Display with inverse video ? */
1289 #if GTK_MAJOR_VERSION < 2
1290 if (prefs.gui_hex_dump_highlight_style) {
1291 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1292 /* Do we start in reverse? */
1293 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1294 fg = reverse ? &WHITE : &BLACK;
1295 bg = reverse ? &BLACK : &WHITE;
1297 k = i + BYTE_VIEW_WIDTH;
1299 /* Print the hex bit */
1302 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1303 line[cur++] = hexchars[pd[i] & 0x0f];
1305 line[cur++] = ' '; line[cur++] = ' ';
1308 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1309 /* Have we gone from reverse to plain? */
1310 if (reverse && (reverse != newreverse)) {
1311 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1316 /* Inter byte space if not at end of line */
1319 /* insert a space every BYTE_VIEW_SEP bytes */
1320 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1324 /* Have we gone from plain to reversed? */
1325 if (!reverse && (reverse != newreverse)) {
1326 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1331 reverse = newreverse;
1333 /* Print remaining part of line */
1334 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1336 /* Print some space at the end of the line */
1337 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1338 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, cur);
1341 /* Print the ASCII bit */
1343 /* Do we start in reverse? */
1344 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1345 fg = reverse ? &WHITE : &BLACK;
1346 bg = reverse ? &BLACK : &WHITE;
1349 if (encoding == CHAR_ASCII) {
1352 else if (encoding == CHAR_EBCDIC) {
1353 c = EBCDIC_to_ASCII1(pd[i]);
1356 g_assert_not_reached();
1358 line[cur++] = isprint(c) ? c : '.';
1363 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1364 /* Have we gone from reverse to plain? */
1365 if (reverse && (reverse != newreverse)) {
1366 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1372 /* insert a space every BYTE_VIEW_SEP bytes */
1373 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1377 /* Have we gone from plain to reversed? */
1378 if (!reverse && (reverse != newreverse)) {
1379 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1384 reverse = newreverse;
1386 /* Print remaining part of line */
1387 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1391 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1394 gtk_text_insert(bv_text, user_font_get_regular(), NULL, NULL, line, -1);
1395 /* Do we start in bold? */
1396 cur_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1398 k = i + BYTE_VIEW_WIDTH;
1400 /* Print the hex bit */
1403 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1404 line[cur++] = hexchars[pd[i] & 0x0f];
1406 line[cur++] = ' '; line[cur++] = ' ';
1410 /* insert a space every BYTE_VIEW_SEP bytes */
1411 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1412 /* Did we cross a bold/plain boundary? */
1413 new_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1414 if (cur_font != new_font) {
1415 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1416 cur_font = new_font;
1421 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1425 /* Print the ASCII bit */
1426 cur_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1429 if (encoding == CHAR_ASCII) {
1432 else if (encoding == CHAR_EBCDIC) {
1433 c = EBCDIC_to_ASCII1(pd[i]);
1436 g_assert_not_reached();
1438 line[cur++] = isprint(c) ? c : '.';
1443 /* insert a space every BYTE_VIEW_SEP bytes */
1444 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1445 /* Did we cross a bold/plain boundary? */
1446 new_font = ((i >= bstart && i < bend) || (i >= astart && i < aend)) ? user_font_get_bold() : user_font_get_regular();
1447 if (cur_font != new_font) {
1448 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1449 cur_font = new_font;
1455 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, -1);
1458 if (prefs.gui_hex_dump_highlight_style)
1459 revstyle = "reverse";
1463 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1,
1465 /* Do we start in reverse? */
1466 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1468 k = i + BYTE_VIEW_WIDTH;
1470 /* Print the hex bit */
1473 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1474 line[cur++] = hexchars[pd[i] & 0x0f];
1476 line[cur++] = ' '; line[cur++] = ' ';
1479 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1480 /* Have we gone from reverse to plain? */
1481 if (reverse && (reverse != newreverse)) {
1482 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1486 /* Inter byte space if not at end of line */
1489 /* insert a space every BYTE_VIEW_SEP bytes */
1490 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1494 /* Have we gone from plain to reversed? */
1495 if (!reverse && (reverse != newreverse)) {
1496 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1498 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1501 reverse = newreverse;
1503 /* Print remaining part of line */
1504 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1505 reverse ? revstyle : "plain",
1508 /* Print some space at the end of the line */
1509 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1510 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1514 /* Print the ASCII bit */
1516 /* Do we start in reverse? */
1517 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1520 if (encoding == CHAR_ASCII) {
1523 else if (encoding == CHAR_EBCDIC) {
1524 c = EBCDIC_to_ASCII1(pd[i]);
1527 g_assert_not_reached();
1529 line[cur++] = isprint(c) ? c : '.';
1534 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1535 /* Have we gone from reverse to plain? */
1536 if (reverse && (reverse != newreverse)) {
1537 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1538 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1540 g_free( (gpointer) convline);
1544 /* insert a space every BYTE_VIEW_SEP bytes */
1545 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1549 /* Have we gone from plain to reversed? */
1550 if (!reverse && (reverse != newreverse)) {
1551 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1552 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1554 g_free( (gpointer) convline);
1557 reverse = newreverse;
1559 /* Print remaining part of line */
1560 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1561 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1562 reverse ? revstyle : "plain",
1564 g_free( (gpointer) convline);
1567 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1572 /* We're done printing the packets; destroy the progress bar if
1574 if (progbar != NULL)
1575 destroy_progress_dlg(progbar);
1577 /* scroll text into position */
1578 #if GTK_MAJOR_VERSION < 2
1579 gtk_text_thaw(bv_text); /* must thaw before adjusting scroll bars */
1584 linenum = bstart / BYTE_VIEW_WIDTH;
1585 scrollval = MIN(linenum * user_font_get_regular_height(),
1586 bv_text->vadj->upper - bv_text->vadj->page_size);
1588 gtk_adjustment_set_value(bv_text->vadj, scrollval);
1592 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1593 gtk_text_buffer_delete_mark(buf, mark);
1599 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1600 field_info *finfo, guint len)
1602 /* do the initial printing and save the information needed */
1603 /* to redraw the display if preferences change. */
1605 int bstart = -1, bend = -1, blen = -1;
1606 int astart = -1, aend = -1, alen = -1;
1608 if (finfo != NULL) {
1609 bstart = finfo->start;
1610 blen = finfo->length;
1611 astart = finfo->appendix_start;
1612 alen = finfo->appendix_length;
1614 if (bstart >= 0 && blen >= 0) {
1615 bend = bstart + blen;
1617 if (astart >= 0 && alen >= 0) {
1618 aend = astart + alen;
1621 /* save the information needed to redraw the text */
1622 /* should we save the fd & finfo pointers instead ?? */
1623 OBJECT_SET_DATA(bv, E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1624 OBJECT_SET_DATA(bv, E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1625 OBJECT_SET_DATA(bv, E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1626 OBJECT_SET_DATA(bv, E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1627 OBJECT_SET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY,
1628 GUINT_TO_POINTER((guint)fd->flags.encoding));
1630 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1634 * Redraw the text using the saved information; usually called if
1635 * the preferences have changed.
1638 packet_hex_reprint(GtkWidget *bv)
1640 int start, end, encoding;
1645 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1646 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1647 astart = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_APP_START_KEY));
1648 aend = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_APP_END_KEY));
1649 data = get_byte_view_data_and_length(bv, &len);
1650 g_assert(data != NULL);
1651 encoding = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY));
1653 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1656 /* List of all protocol tree widgets, so we can globally set the selection
1657 mode and font of all of them. */
1658 static GList *ptree_widgets;
1660 /* Add a protocol tree widget to the list of protocol tree widgets. */
1661 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1664 remember_ptree_widget(GtkWidget *ptreew)
1666 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1668 /* Catch the "destroy" event on the widget, so that we remove it from
1669 the list when it's destroyed. */
1670 SIGNAL_CONNECT(ptreew, "destroy", forget_ptree_widget, NULL);
1673 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1675 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1677 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1680 /* Set the selection mode of a given packet tree window. */
1682 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1684 #if GTK_MAJOR_VERSION >= 2
1685 GtkTreeSelection *selection;
1687 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1689 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1690 I think "browse" in Wireshark makes more sense than "SINGLE" in
1693 #if GTK_MAJOR_VERSION < 2
1694 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1695 GTK_SELECTION_SINGLE);
1697 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1701 #if GTK_MAJOR_VERSION < 2
1702 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1703 GTK_SELECTION_BROWSE);
1705 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1711 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1713 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1716 /* Set the selection mode of all packet tree windows. */
1718 set_ptree_sel_browse_all(gboolean val)
1720 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1723 #if GTK_MAJOR_VERSION < 2
1725 set_ptree_style_cb(gpointer data, gpointer user_data)
1727 gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
1731 set_ptree_font_cb(gpointer data, gpointer user_data)
1733 gtk_widget_modify_font((GtkWidget *)data,
1734 (PangoFontDescription *)user_data);
1739 set_ptree_font_all(FONT_TYPE *font)
1741 #if GTK_MAJOR_VERSION < 2
1744 style = gtk_style_new();
1745 gdk_font_unref(style->font);
1749 g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
1751 /* Now nuke the old style and replace it with the new one. */
1752 gtk_style_unref(item_style);
1755 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1760 gboolean colors_ok = FALSE;
1761 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1762 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1763 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1764 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1766 void proto_draw_colors_init(void)
1772 get_color(&expert_color_chat);
1773 get_color(&expert_color_note);
1774 get_color(&expert_color_warn);
1775 get_color(&expert_color_error);
1781 #if GTK_MAJOR_VERSION >= 2
1782 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1783 GtkCellRenderer *cell,
1784 GtkTreeModel *tree_model,
1790 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1793 proto_draw_colors_init();
1796 /* for the various possible attributes, see:
1797 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1799 * color definitions can be found at:
1800 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1801 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1804 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1805 * weight/style: doesn't take any effect
1808 /* for each field, we have to reset the renderer attributes */
1809 g_object_set (cell, "foreground-set", FALSE, NULL);
1811 g_object_set (cell, "background", "white", NULL);
1812 g_object_set (cell, "background-set", TRUE, NULL);
1814 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1815 g_object_set (cell, "underline-set", FALSE, NULL);
1817 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1818 g_object_set (cell, "style-set", FALSE, NULL);*/
1820 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1821 g_object_set (cell, "weight-set", FALSE, NULL);*/
1823 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1824 /* we use "[...]" to mark generated items, no need to change things here */
1826 /* as some fonts don't support italic, don't use this */
1827 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1828 g_object_set (cell, "style-set", TRUE, NULL);
1830 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1831 g_object_set (cell, "weight-set", TRUE, NULL);*/
1834 if(fi->hfinfo->type == FT_PROTOCOL) {
1835 g_object_set (cell, "background", "gray90", NULL);
1836 g_object_set (cell, "background-set", TRUE, NULL);
1837 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1838 g_object_set (cell, "weight-set", TRUE, NULL);*/
1841 if((fi->hfinfo->type == FT_FRAMENUM) ||
1842 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1843 g_object_set (cell, "foreground", "blue", NULL);
1844 g_object_set (cell, "foreground-set", TRUE, NULL);
1846 g_object_set (cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1847 g_object_set (cell, "underline-set", TRUE, NULL);
1850 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1851 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1853 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1854 g_object_set (cell, "background-set", TRUE, NULL);
1857 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1858 g_object_set (cell, "background-set", TRUE, NULL);
1861 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1862 g_object_set (cell, "background-set", TRUE, NULL);
1865 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1866 g_object_set (cell, "background-set", TRUE, NULL);
1869 g_assert_not_reached();
1876 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1878 GtkWidget *tv_scrollw, *tree_view;
1879 #if GTK_MAJOR_VERSION >= 2
1880 GtkTreeStore *store;
1881 GtkCellRenderer *renderer;
1882 GtkTreeViewColumn *column;
1887 tv_scrollw = scrolled_window_new(NULL, NULL);
1888 #if GTK_MAJOR_VERSION >= 2
1889 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1893 #if GTK_MAJOR_VERSION < 2
1894 tree_view = ctree_new(1, 0);
1895 SIGNAL_CONNECT(tree_view, "key-press-event", toggle_tree, NULL );
1896 SIGNAL_CONNECT(tree_view, "tree-expand", expand_tree, NULL );
1897 SIGNAL_CONNECT(tree_view, "tree-collapse", collapse_tree, NULL );
1898 /* I need this next line to make the widget work correctly with hidden
1899 * column titles and GTK_SELECTION_BROWSE */
1900 gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1902 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1903 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1904 g_object_unref(G_OBJECT(store));
1905 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1906 renderer = gtk_cell_renderer_text_new();
1907 g_object_set (renderer, "ypad", 0, NULL);
1908 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1909 -1, "Name", renderer,
1911 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1913 gtk_tree_view_column_set_cell_data_func(column,
1919 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1920 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1921 SIGNAL_CONNECT(tree_view, "row-expanded", expand_tree, NULL);
1922 SIGNAL_CONNECT(tree_view, "row-collapsed", collapse_tree, NULL);
1924 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1925 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1926 #if GTK_MAJOR_VERSION < 2
1927 if(item_style == NULL) {
1928 item_style = gtk_style_new();
1929 gdk_font_unref(item_style->font);
1930 item_style->font = user_font_get_regular();
1933 gtk_widget_set_style(tree_view, item_style);
1935 gtk_widget_modify_font(tree_view, user_font_get_regular());
1937 remember_ptree_widget(tree_view);
1939 *tree_view_p = tree_view;
1944 #if GTK_MAJOR_VERSION < 2
1945 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1947 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1951 for(i=0; i < num_tree_types; i++) {
1952 tree_is_expanded[i] = TRUE;
1954 #if GTK_MAJOR_VERSION < 2
1955 proto_tree_draw(protocol_tree, tree_view);
1956 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
1958 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1962 #if GTK_MAJOR_VERSION < 2
1963 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1965 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1969 for(i=0; i < num_tree_types; i++) {
1970 tree_is_expanded[i] = FALSE;
1972 #if GTK_MAJOR_VERSION < 2
1973 proto_tree_draw(protocol_tree, tree_view);
1975 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1980 struct proto_tree_draw_info {
1981 #if GTK_MAJOR_VERSION < 2
1983 GtkCTreeNode *ctree_node;
1985 GtkTreeView *tree_view;
1991 main_proto_tree_draw(proto_tree *protocol_tree)
1993 proto_tree_draw(protocol_tree, tree_view);
1998 tree_view_follow_link(field_info *fi)
2002 if(fi->hfinfo->type == FT_FRAMENUM) {
2003 cf_goto_frame(&cfile, fi->value.value.uinteger);
2005 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
2006 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
2007 browser_open_url(url);
2013 /* If the user selected a position in the tree view, try to find
2014 * the item in the GUI proto_tree that corresponds to that byte, and
2017 tree_view_select(GtkWidget *widget, GdkEventButton *event)
2019 #if GTK_MAJOR_VERSION < 2
2027 if(gtk_clist_get_selection_info(GTK_CLIST(widget),
2028 (gint) (((GdkEventButton *)event)->x),
2029 (gint) (((GdkEventButton *)event)->y),
2032 ctree = GTK_CTREE(widget);
2034 node = gtk_ctree_node_nth(ctree, row);
2037 gtk_ctree_select(ctree, node);
2039 /* if that's a doubleclick, try to follow the link */
2040 if(event->type == GDK_2BUTTON_PRESS) {
2041 fi = gtk_ctree_node_get_row_data(ctree, node);
2042 tree_view_follow_link(fi);
2048 GtkTreeSelection *sel;
2051 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
2052 (gint) (((GdkEventButton *)event)->x),
2053 (gint) (((GdkEventButton *)event)->y),
2054 &path, NULL, NULL, NULL))
2056 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2057 gtk_tree_selection_select_path(sel, path);
2059 /* if that's a doubleclick, try to follow the link */
2060 if(event->type == GDK_2BUTTON_PRESS) {
2061 GtkTreeModel *model;
2065 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
2066 gtk_tree_model_get(model, &iter, 1, &fi, -1);
2067 tree_view_follow_link(fi);
2077 /* fill the whole protocol tree with the string values */
2079 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
2081 #if GTK_MAJOR_VERSION >= 2
2082 GtkTreeStore *store;
2084 struct proto_tree_draw_info info;
2086 #if GTK_MAJOR_VERSION < 2
2087 info.ctree = GTK_CTREE(tree_view);
2088 info.ctree_node = NULL;
2090 gtk_clist_freeze(GTK_CLIST(tree_view));
2092 info.tree_view = GTK_TREE_VIEW(tree_view);
2095 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
2099 * Clear out any crud left over in the display of the protocol
2100 * tree, by removing all nodes from the tree.
2101 * This is how it's done in testgtk.c in GTK+.
2103 #if GTK_MAJOR_VERSION < 2
2104 gtk_clist_clear(GTK_CLIST(tree_view));
2106 gtk_tree_store_clear(store);
2109 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2111 #if GTK_MAJOR_VERSION < 2
2112 gtk_clist_thaw(GTK_CLIST(tree_view));
2117 /* fill a single protocol tree item with the string value */
2119 proto_tree_draw_node(proto_node *node, gpointer data)
2121 struct proto_tree_draw_info info;
2122 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2124 field_info *fi = PITEM_FINFO(node);
2125 gchar label_str[ITEM_LABEL_LENGTH];
2127 gboolean is_leaf, is_expanded;
2128 #if GTK_MAJOR_VERSION < 2
2129 GtkCTreeNode *parent;
2131 GtkTreeStore *store;
2136 if (PROTO_ITEM_IS_HIDDEN(node))
2139 /* was a free format label produced? */
2141 label_ptr = fi->rep->representation;
2143 else { /* no, make a generic label */
2144 label_ptr = label_str;
2145 proto_item_fill_label(fi, label_str);
2148 if (node->first_child != NULL) {
2150 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2151 if (tree_is_expanded[fi->tree_type]) {
2155 is_expanded = FALSE;
2160 is_expanded = FALSE;
2163 if(PROTO_ITEM_IS_GENERATED(node)) {
2164 label_ptr = g_strdup_printf("[%s]", label_ptr);
2167 #if GTK_MAJOR_VERSION < 2
2168 info.ctree = parent_info->ctree;
2169 parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
2170 &label_ptr, 5, NULL, NULL, NULL, NULL,
2171 is_leaf, is_expanded );
2173 gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
2175 info.tree_view = parent_info->tree_view;
2176 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2177 gtk_tree_store_append(store, &iter, parent_info->iter);
2178 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2181 if(PROTO_ITEM_IS_GENERATED(node)) {
2186 #if GTK_MAJOR_VERSION < 2
2187 info.ctree_node = parent;
2191 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2192 #if GTK_MAJOR_VERSION >= 2
2193 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2195 /* #if GTK_MINOR_VERSION >= 2 */
2196 gtk_tree_view_expand_to_path(info.tree_view, path);
2198 gtk_tree_view_expand_row(info.tree_view, path, FALSE);
2201 gtk_tree_view_collapse_row(info.tree_view, path);
2202 gtk_tree_path_free(path);
2208 * Clear the hex dump and protocol tree panes.
2211 clear_tree_and_hex_views(void)
2213 /* Clear the hex dump by getting rid of all the byte views. */
2214 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
2215 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
2217 /* Add a placeholder byte view so that there's at least something
2218 displayed in the byte view notebook. */
2219 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
2221 /* Clear the protocol tree by removing all nodes in the ctree.
2222 This is how it's done in testgtk.c in GTK+ */
2223 #if GTK_MAJOR_VERSION < 2
2224 gtk_clist_clear(GTK_CLIST(tree_view));
2226 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));