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_ENCODE_KEY "byte_view_encode"
96 #if GTK_MAJOR_VERSION < 2
97 GtkStyle *item_style = NULL;
100 /* gtk_tree_view_expand_to_path doesn't exist in gtk+ v2.0 so we must include it
101 * when building with this version (taken from gtk+ v2.2.4) */
102 #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION == 0
104 * gtk_tree_view_expand_to_path:
105 * @tree_view: A #GtkTreeView.
106 * @path: path to a row.
108 * Expands the row at @path. This will also expand all parent rows of
109 * @path as necessary.
114 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
121 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
122 g_return_if_fail (path != NULL);
124 depth = gtk_tree_path_get_depth (path);
125 indices = gtk_tree_path_get_indices (path);
127 tmp = gtk_tree_path_new ();
128 g_return_if_fail (tmp != NULL);
130 for (i = 0; i < depth; i++)
132 gtk_tree_path_append_index (tmp, indices[i]);
133 gtk_tree_view_expand_row (tree_view, tmp, FALSE);
136 gtk_tree_path_free (tmp);
141 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
142 proto_tree *tree, GtkWidget *tree_view);
145 proto_tree_draw_node(proto_node *node, gpointer data);
147 /* Get the current text window for the notebook. */
149 get_notebook_bv_ptr(GtkWidget *nb_ptr)
154 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
155 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
157 return GTK_BIN(bv_page)->child;
163 * Get the data and length for a byte view, given the byte view page.
164 * Return the pointer, or NULL on error, and set "*data_len" to the length.
167 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
169 tvbuff_t *byte_view_tvb;
170 const guint8 *data_ptr;
172 byte_view_tvb = OBJECT_GET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY);
173 if (byte_view_tvb == NULL)
176 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
177 *data_len = tvb_length(byte_view_tvb);
182 * Set the current text window for the notebook to the window that
183 * refers to a particular tvbuff.
186 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
189 GtkWidget *bv_page, *bv;
193 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
195 bv = GTK_BIN(bv_page)->child;
196 bv_tvb = OBJECT_GET_DATA(bv, E_BYTE_VIEW_TVBUFF_KEY);
199 gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
205 /* Redraw a given byte view window. */
207 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
213 bv = get_notebook_bv_ptr(nb);
215 data = get_byte_view_data_and_length(bv, &len);
217 packet_hex_print(bv, data, fd, finfo, len);
221 /* Redraw all byte view windows. */
223 redraw_hex_dump_all(void)
225 if (cfile.current_frame != NULL)
226 redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
228 redraw_hex_dump_packet_wins();
230 #if GTK_MAJOR_VERSION >= 2
231 /* XXX - this is a hack, to workaround a bug in GTK2.x!
232 when changing the font size, even refilling of the corresponding
233 gtk_text_buffer doesn't seem to trigger an update.
234 The only workaround is to freshly select the frame, which will remove any
235 existing notebook tabs and "restart" the whole byte view again. */
236 if (cfile.current_frame != NULL)
237 cf_goto_frame(&cfile, cfile.current_frame->num);
241 #if GTK_MAJOR_VERSION < 2
243 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
246 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
247 GtkTreePath *path _U_, gpointer user_data _U_)
251 #if GTK_MAJOR_VERSION >= 2
254 model = gtk_tree_view_get_model(tree_view);
255 gtk_tree_model_get(model, iter, 1, &finfo, -1);
257 finfo = gtk_ctree_node_get_row_data( ctree, node);
262 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
263 * are thus presumably leaf nodes and cannot be expanded.
265 if (finfo->tree_type != -1) {
266 g_assert(finfo->tree_type >= 0 &&
267 finfo->tree_type < num_tree_types);
268 tree_is_expanded[finfo->tree_type] = TRUE;
272 #if GTK_MAJOR_VERSION < 2
274 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
277 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
278 GtkTreePath *path _U_, gpointer user_data _U_)
282 #if GTK_MAJOR_VERSION >= 2
285 model = gtk_tree_view_get_model(tree_view);
286 gtk_tree_model_get(model, iter, 1, &finfo, -1);
288 finfo = gtk_ctree_node_get_row_data( ctree, node);
293 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
294 * are thus presumably leaf nodes and cannot be collapsed.
296 if (finfo->tree_type != -1) {
297 g_assert(finfo->tree_type >= 0 &&
298 finfo->tree_type < num_tree_types);
299 tree_is_expanded[finfo->tree_type] = FALSE;
303 #if GTK_MAJOR_VERSION < 2
305 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
307 if (event->keyval != GDK_Return)
309 gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
313 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
314 #define BYTES_PER_LINE 16 /* max byte values in a line */
315 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
316 /* max number of characters hex dump takes -
317 2 digits plus trailing blank
318 plus separator between first and
320 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
321 /* number of characters those bytes take;
322 3 characters per byte of hex dump,
323 2 blanks separating hex from ASCII,
324 1 character per byte of ASCII dump */
325 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
326 /* number of characters per line;
327 offset, 2 blanks separating offset
328 from data dump, data dump */
330 /* Which byte the offset is referring to. Associates
331 * whitespace with the preceding digits. */
333 byte_num(int offset, int start_point)
335 return (offset - start_point) / 3;
338 #if GTK_MAJOR_VERSION >= 2
339 struct field_lookup_info {
345 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
349 struct field_lookup_info *fli = (struct field_lookup_info *)data;
351 gtk_tree_model_get(model, iter, 1, &fi, -1);
359 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
361 struct field_lookup_info fli;
363 g_assert(finfo != NULL);
365 model = gtk_tree_view_get_model(tree_view);
367 gtk_tree_model_foreach(model, lookup_finfo, &fli);
369 return gtk_tree_model_get_path(model, &fli.iter);
374 /* If the user selected a certain byte in the byte view, try to find
375 * the item in the GUI proto_tree that corresponds to that byte, and:
377 * if we succeed, select it, and return TRUE;
378 * if we fail, return FALSE. */
380 byte_view_select(GtkWidget *widget, GdkEventButton *event)
383 #if GTK_MAJOR_VERSION < 2
385 GtkCTreeNode *node, *parent;
386 GtkText *bv = GTK_TEXT(widget);
388 GtkTreeView *tree_view;
389 GtkTextView *bv = GTK_TEXT_VIEW(widget);
407 * Get the number of digits of offset being displayed, and
408 * compute the columns of various parts of the display.
410 ndigits = GPOINTER_TO_UINT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY));
413 * The column of the first hex digit in the first half.
414 * That starts after "ndigits" digits of offset and two
417 digits_start_1 = ndigits + 2;
420 * The column of the last hex digit in the first half.
421 * There are BYTES_PER_LINE/2 bytes displayed in the first
422 * half; there are 2 characters per byte, plus a separating
423 * blank after all but the last byte's characters.
425 * Then subtract 1 to get the last column of the first half
426 * rather than the first column after the first half.
428 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
429 (BYTES_PER_LINE/2 - 1) - 1;
432 * The column of the first hex digit in the second half.
433 * Add back the 1 to get the first column after the first
434 * half, and then add 2 for the 2 separating blanks between
437 digits_start_2 = digits_end_1 + 3;
440 * The column of the last hex digit in the second half.
441 * Add the same value we used to get "digits_end_1" from
444 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
445 (BYTES_PER_LINE/2 - 1) - 1;
448 * The column of the first "text dump" character in the first half.
449 * Add back the 1 to get the first column after the second
450 * half's hex dump, and then add 3 for the 3 separating blanks
451 * between the hex and text dummp.
453 text_start_1 = digits_end_2 + 4;
456 * The column of the last "text dump" character in the first half.
457 * There are BYTES_PER_LINE/2 bytes displayed in the first
458 * half; there is 1 character per byte.
460 * Then subtract 1 to get the last column of the first half
461 * rather than the first column after the first half.
463 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
466 * The column of the first "text dump" character in the second half.
467 * Add back the 1 to get the first column after the first half,
468 * and then add 1 for the separating blank between the halves.
470 text_start_2 = text_end_1 + 2;
473 * The column of the last "text dump" character in second half.
474 * Add the same value we used to get "text_end_1" from
477 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
479 tree = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_PTR);
482 * Somebody clicked on the dummy byte view; do nothing.
486 #if GTK_MAJOR_VERSION < 2
487 ctree = GTK_CTREE(OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_VIEW_PTR));
489 tree_view = GTK_TREE_VIEW(OBJECT_GET_DATA(widget,
490 E_BYTE_VIEW_TREE_VIEW_PTR));
493 #if GTK_MAJOR_VERSION < 2
494 /* Given the mouse (x,y) and the current GtkText (h,v)
495 * adjustments, and the size of the font, figure out
496 * which text column/row the user selected. This could be off
497 * if the bold version of the font is bigger than the
498 * regular version of the font. */
499 column = (int) ((bv->hadj->value + event->x) / user_font_get_regular_width());
500 row = (int) ((bv->vadj->value + event->y) / user_font_get_regular_height());
502 /* get the row/column selected */
503 gtk_text_view_window_to_buffer_coords(bv,
504 gtk_text_view_get_window_type(bv, event->window),
505 (gint) event->x, (gint) event->y, &x, &y);
506 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
507 row = gtk_text_iter_get_line(&iter);
508 column = gtk_text_iter_get_line_offset(&iter);
511 /* Given the column and row, determine which byte offset
512 * the user clicked on. */
513 if (column >= digits_start_1 && column <= digits_end_1) {
514 byte = byte_num(column, digits_start_1);
519 else if (column >= digits_start_2 && column <= digits_end_2) {
520 byte = byte_num(column, digits_start_2);
526 else if (column >= text_start_1 && column <= text_end_1) {
527 byte = column - text_start_1;
529 else if (column >= text_start_2 && column <= text_end_2) {
530 byte = 8 + column - text_start_2;
533 /* The user didn't select a hex digit or
534 * text-dump character. */
538 /* Add the number of bytes from the previous rows. */
541 /* Get the data source tvbuff */
542 tvb = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TVBUFF_KEY);
544 #if GTK_MAJOR_VERSION < 2
545 return highlight_field(tvb, byte, ctree, tree);
547 return highlight_field(tvb, byte, tree_view, tree);
551 /* This highlights the field in the proto tree that is at position byte */
552 #if GTK_MAJOR_VERSION < 2
554 highlight_field(tvbuff_t *tvb, gint byte, GtkCTree *ctree,
558 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
562 #if GTK_MAJOR_VERSION < 2
563 GtkCTreeNode *node, *parent;
566 GtkTreePath *first_path, *path;
568 struct field_lookup_info fli;
572 /* Find the finfo that corresponds to our byte. */
573 finfo = proto_find_field_from_offset(tree, byte, tvb);
579 #if GTK_MAJOR_VERSION < 2
580 node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
583 /* Expand and select our field's row */
584 gtk_ctree_expand(ctree, node);
585 gtk_ctree_select(ctree, node);
586 expand_tree(ctree, node, NULL);
588 /* ... and its parents */
589 parent = GTK_CTREE_ROW(node)->parent;
591 gtk_ctree_expand(ctree, parent);
592 expand_tree(ctree, parent, NULL);
593 parent = GTK_CTREE_ROW(parent)->parent;
596 /* And position the window so the selection is visible.
597 * Position the selection in the middle of the viewable
599 gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
603 model = gtk_tree_view_get_model(tree_view);
605 gtk_tree_model_foreach(model, lookup_finfo, &fli);
607 /* Expand our field's row */
608 first_path = gtk_tree_model_get_path(model, &fli.iter);
609 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
610 expand_tree(tree_view, &fli.iter, NULL, NULL);
612 /* ... and its parents */
613 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
614 path = gtk_tree_model_get_path(model, &parent);
615 gtk_tree_view_expand_row(tree_view, path, FALSE);
616 expand_tree(tree_view, &parent, NULL, NULL);
618 gtk_tree_path_free(path);
621 /* select our field's row */
622 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
625 /* And position the window so the selection is visible.
626 * Position the selection in the middle of the viewable
628 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
630 gtk_tree_path_free(first_path);
636 /* Calls functions for different mouse-button presses. */
638 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
640 GdkEventButton *event_button = NULL;
642 if(widget == NULL || event == NULL || data == NULL) {
646 if(event->type == GDK_BUTTON_PRESS) {
647 event_button = (GdkEventButton *) event;
649 /* To qoute the "Gdk Event Structures" doc:
650 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
651 switch(event_button->button) {
654 return byte_view_select(widget, event_button);
656 return popup_menu_handler(widget, event, data);
670 byte_nb = gtk_notebook_new();
671 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
673 /* this will only have an effect, if no tabs are shown */
674 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
676 /* set the tabs scrollable, if they don't fit into the pane */
677 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
679 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
680 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
682 /* Add a placeholder byte view so that there's at least something
683 displayed in the byte view notebook. */
684 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
690 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
692 const guint8 *byte_data;
695 byte_data = get_byte_view_data_and_length(bv, &byte_len);
696 if (byte_data == NULL) {
697 /* This must be the dummy byte view if no packet is selected. */
700 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
704 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
705 proto_tree *tree, GtkWidget *tree_view)
707 GtkWidget *byte_view, *byte_scrollw, *label;
708 #if GTK_MAJOR_VERSION >= 2
713 /* Byte view. Create a scrolled window for the text. */
714 byte_scrollw = scrolled_window_new(NULL, NULL);
715 #if GTK_MAJOR_VERSION < 2
716 /* The horizontal scrollbar of the scroll-window doesn't seem
717 * to affect the GtkText widget at all, even when line wrapping
718 * is turned off in the GtkText widget and there is indeed more
719 * horizontal data. */
720 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
721 /* Horizontal */GTK_POLICY_NEVER,
722 /* Vertical*/ GTK_POLICY_ALWAYS);
724 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
727 /* Add scrolled pane to tabbed window */
728 label = gtk_label_new(name);
729 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
731 gtk_widget_show(byte_scrollw);
733 #if GTK_MAJOR_VERSION < 2
734 byte_view = gtk_text_new(NULL, NULL);
735 gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
736 gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
737 gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
739 byte_view = gtk_text_view_new();
740 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
741 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
742 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
743 style = gtk_widget_get_style(GTK_WIDGET(byte_view));
744 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
745 gtk_text_buffer_create_tag(buf, "reverse",
746 "font-desc", user_font_get_regular(),
747 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
748 "background-gdk", &style->base[GTK_STATE_SELECTED],
750 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
752 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, tvb);
753 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
755 SIGNAL_CONNECT(byte_view, "show", byte_view_realize_cb, NULL);
756 SIGNAL_CONNECT(byte_view, "button_press_event", byte_view_button_press_cb,
757 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
759 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, tree);
760 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
762 gtk_widget_show(byte_view);
764 /* no tabs if this is the first page */
765 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
766 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
768 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
770 /* set this page (this will print the packet data) */
771 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb),
772 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
778 add_main_byte_views(epan_dissect_t *edt)
780 add_byte_views(edt, tree_view, byte_nb_ptr);
784 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
785 GtkWidget *byte_nb_ptr)
791 * Get rid of all the old notebook tabs.
793 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
794 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
797 * Add to the specified byte view notebook tabs for hex dumps
798 * of all the data sources for the specified frame.
800 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
802 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
807 * Initially select the first byte view.
809 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
814 static GtkWidget *savehex_dlg=NULL;
817 savehex_dlg_destroy_cb(void)
823 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, int data_type)
828 const guint8 *data_p = NULL;
829 GString *ASCII_representation = g_string_new("");
830 GString *byte_str = g_string_new("");
831 GString *text_str = g_string_new("");
833 bv = get_notebook_bv_ptr(byte_nb_ptr);
835 /* shouldn't happen */
836 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
840 data_p = get_byte_view_data_and_length(bv, &len);
841 g_assert(data_p != NULL);
843 g_string_sprintfa(byte_str,"%04x ",i); /* Offset 0000 */
844 for (i=0; i<len; i++){
846 if (isprint(*data_p)) {
847 g_string_sprintfa(ASCII_representation,"%c", *data_p);
852 g_string_sprintfa(ASCII_representation,"\n");
858 g_string_sprintfa(ASCII_representation,"%c",isprint(*data_p) ? *data_p : '.');
860 g_string_sprintfa(byte_str," %02x",*data_p++);
861 if ((i+1)%16==0 && i!=0){
862 g_string_sprintfa(byte_str," %s\n%04x ",ASCII_representation->str,i+1);
863 g_string_sprintfa(text_str,"%s",ASCII_representation->str);
865 g_string_assign (ASCII_representation,"");
869 if(ASCII_representation->len){
870 for (i=ASCII_representation->len; i<16; i++){
871 g_string_sprintfa(byte_str," ");
873 g_string_sprintfa(byte_str," %s\n",ASCII_representation->str);
874 g_string_sprintfa(text_str,"%s",ASCII_representation->str);
876 /* Now that we have the byte data, copy it into the default clipboard */
878 copy_to_clipboard(text_str);
882 copy_to_clipboard(byte_str);
884 g_string_free(byte_str, TRUE); /* Free the memory */
885 g_string_free(text_str, TRUE); /* Free the memory */
886 g_string_free(ASCII_representation, TRUE); /* Free the memory */
889 /* save the current highlighted hex data */
891 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
894 int fd, start, end, len;
895 const guint8 *data_p = NULL;
896 const char *file = NULL;
898 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
899 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
901 file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(savehex_dlg));
904 if (!file ||! *file) {
905 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
909 /* Must check if file name exists first */
911 bv = get_notebook_bv_ptr(byte_nb_ptr);
913 /* shouldn't happen */
914 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
918 * Retrieve the info we need
920 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
921 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
922 data_p = get_byte_view_data_and_length(bv, &len);
924 if (data_p == NULL || start == -1 || start > end) {
925 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
926 "No data selected to save!");
930 fd = eth_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
932 open_failure_alert_box(file, errno, TRUE);
935 if (eth_write(fd, data_p + start, end - start) < 0) {
936 write_failure_alert_box(file, errno);
940 if (eth_close(fd) < 0) {
941 write_failure_alert_box(file, errno);
945 /* Get rid of the dialog box */
946 window_destroy(GTK_WIDGET(savehex_dlg));
949 /* Launch the dialog box to put up the file selection box etc */
950 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
953 const guint8 *data_p = NULL;
959 #if GTK_MAJOR_VERSION >= 2 && _WIN32
960 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
964 /* don't show up the dialog, if no data has to be saved */
965 bv = get_notebook_bv_ptr(byte_nb_ptr);
967 /* shouldn't happen */
968 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
971 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
972 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
973 data_p = get_byte_view_data_and_length(bv, &len);
975 if (data_p == NULL || start == -1 || start > end) {
976 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
980 /* if the window is already open, bring it to front */
982 reactivate_window(savehex_dlg);
987 * Build the dialog box we need.
989 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
992 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
993 end - start, plurality(end - start, "byte", "bytes"));
994 dlg_lb = gtk_label_new(label);
996 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
997 gtk_widget_show(dlg_lb);
999 SIGNAL_CONNECT(savehex_dlg, "destroy", savehex_dlg_destroy_cb, NULL);
1001 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
1002 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1003 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1005 window_destroy(savehex_dlg);
1008 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
1009 pointer to the file selection box widget */
1010 SIGNAL_CONNECT(GTK_FILE_SELECTION (savehex_dlg)->ok_button, "clicked",
1011 savehex_save_clicked_cb, savehex_dlg);
1013 window_set_cancel_button(savehex_dlg,
1014 GTK_FILE_SELECTION(savehex_dlg)->cancel_button, window_cancel_button_cb);
1016 SIGNAL_CONNECT(savehex_dlg, "delete_event", window_delete_event_cb, NULL);
1018 gtk_file_selection_set_filename(GTK_FILE_SELECTION(savehex_dlg), "");
1020 gtk_widget_show_all(savehex_dlg);
1021 window_present(savehex_dlg);
1027 /* Update the progress bar this many times when reading a file. */
1028 #define N_PROGBAR_UPDATES 100
1032 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1033 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1034 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1035 * presumably why there's a progress bar for it.)
1037 * Perhaps what's needed is a custom widget (either one that lets you stuff
1038 * text into it more quickly, or one that's a "virtual" widget so that the
1039 * text for a row is constructed, via a callback, when the row is to be
1040 * displayed). A custom widget might also let us treat the offset, hex
1041 * data, and ASCII data as three columns, so you can select purely in
1042 * the hex dump column.
1045 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1046 int bend, int encoding)
1048 int i = 0, j, k, cur;
1049 guchar line[MAX_LINE_LEN + 1];
1050 static guchar hexchars[16] = {
1051 '0', '1', '2', '3', '4', '5', '6', '7',
1052 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1054 unsigned int use_digits;
1055 gboolean reverse, newreverse;
1056 #if GTK_MAJOR_VERSION < 2
1057 GdkFont *cur_font, *new_font;
1059 GtkText *bv_text = GTK_TEXT(bv);
1061 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1062 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1064 const char *revstyle;
1067 GtkTextMark *mark = NULL;
1070 progdlg_t *progbar = NULL;
1072 gboolean progbar_stop_flag;
1073 GTimeVal progbar_start_time;
1074 gchar progbar_status_str[100];
1075 int progbar_nextstep;
1076 int progbar_quantum;
1078 #if GTK_MAJOR_VERSION < 2
1079 /* Freeze the text for faster display */
1080 gtk_text_freeze(bv_text);
1082 /* Clear out the text */
1083 gtk_text_set_point(bv_text, 0);
1084 /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
1085 http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
1086 http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
1087 for more information */
1088 gtk_adjustment_set_value(bv_text->vadj, 0.0);
1089 gtk_text_forward_delete(bv_text, gtk_text_get_length(bv_text));
1091 gtk_text_buffer_set_text(buf, "", 0);
1092 gtk_text_buffer_get_start_iter(buf, &iter);
1096 * How many of the leading digits of the offset will we supply?
1097 * We always supply at least 4 digits, but if the maximum offset
1098 * won't fit in 4 digits, we use as many digits as will be needed.
1100 if (((len - 1) & 0xF0000000) != 0)
1101 use_digits = 8; /* need all 8 digits */
1102 else if (((len - 1) & 0x0F000000) != 0)
1103 use_digits = 7; /* need 7 digits */
1104 else if (((len - 1) & 0x00F00000) != 0)
1105 use_digits = 6; /* need 6 digits */
1106 else if (((len - 1) & 0x000F0000) != 0)
1107 use_digits = 5; /* need 5 digits */
1109 use_digits = 4; /* we'll supply 4 digits */
1111 /* Record the number of digits in this text view. */
1112 OBJECT_SET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1114 /* Update the progress bar when it gets to this value. */
1115 progbar_nextstep = 0;
1116 /* When we reach the value that triggers a progress bar update,
1117 bump that value by this amount. */
1118 progbar_quantum = len/N_PROGBAR_UPDATES;
1119 /* Progress so far. */
1122 progbar_stop_flag = FALSE;
1123 g_get_current_time(&progbar_start_time);
1126 /* Create the progress bar if necessary.
1127 We check on every iteration of the loop, so that it takes no
1128 longer than the standard time to create it (otherwise, for a
1129 large packet, we might take considerably longer than that standard
1130 time in order to get to the next progress bar step). */
1131 if (progbar == NULL)
1132 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1135 &progbar_start_time,
1138 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1139 when we update it, we have to run the GTK+ main loop to get it
1140 to repaint what's pending, and doing so may involve an "ioctl()"
1141 to see if there's any pending input from an X server, and doing
1142 that for every packet can be costly, especially on a big file. */
1143 if (i >= progbar_nextstep) {
1144 /* let's not divide by zero. I should never be started
1145 * with count == 0, so let's assert that
1148 progbar_val = (gfloat) i / len;
1150 if (progbar != NULL) {
1151 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1152 "%4u of %u bytes", i, len);
1153 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1156 progbar_nextstep += progbar_quantum;
1159 if (progbar_stop_flag) {
1160 /* Well, the user decided to abort the operation. Just stop,
1161 and arrange to return TRUE to our caller, so they know it
1162 was stopped explicitly. */
1166 /* Print the line number */
1171 c = (i >> (j*4)) & 0xF;
1172 line[cur++] = hexchars[c];
1178 /* Display with inverse video ? */
1179 #if GTK_MAJOR_VERSION < 2
1180 if (prefs.gui_hex_dump_highlight_style) {
1181 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1182 /* Do we start in reverse? */
1183 reverse = i >= bstart && i < bend;
1184 fg = reverse ? &WHITE : &BLACK;
1185 bg = reverse ? &BLACK : &WHITE;
1187 k = i + BYTE_VIEW_WIDTH;
1189 /* Print the hex bit */
1192 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1193 line[cur++] = hexchars[pd[i] & 0x0f];
1195 line[cur++] = ' '; line[cur++] = ' ';
1198 newreverse = i >= bstart && i < bend;
1199 /* Have we gone from reverse to plain? */
1200 if (reverse && (reverse != newreverse)) {
1201 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1206 /* Inter byte space if not at end of line */
1209 /* insert a space every BYTE_VIEW_SEP bytes */
1210 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1214 /* Have we gone from plain to reversed? */
1215 if (!reverse && (reverse != newreverse)) {
1216 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1221 reverse = newreverse;
1223 /* Print remaining part of line */
1224 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1226 /* Print some space at the end of the line */
1227 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1228 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, cur);
1231 /* Print the ASCII bit */
1233 /* Do we start in reverse? */
1234 reverse = i >= bstart && i < bend;
1235 fg = reverse ? &WHITE : &BLACK;
1236 bg = reverse ? &BLACK : &WHITE;
1239 if (encoding == CHAR_ASCII) {
1242 else if (encoding == CHAR_EBCDIC) {
1243 c = EBCDIC_to_ASCII1(pd[i]);
1246 g_assert_not_reached();
1248 line[cur++] = isprint(c) ? c : '.';
1253 newreverse = i >= bstart && i < bend;
1254 /* Have we gone from reverse to plain? */
1255 if (reverse && (reverse != newreverse)) {
1256 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1262 /* insert a space every BYTE_VIEW_SEP bytes */
1263 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1267 /* Have we gone from plain to reversed? */
1268 if (!reverse && (reverse != newreverse)) {
1269 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1274 reverse = newreverse;
1276 /* Print remaining part of line */
1277 gtk_text_insert(bv_text, user_font_get_regular(), fg, bg, line, cur);
1281 gtk_text_insert(bv_text, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1284 gtk_text_insert(bv_text, user_font_get_regular(), NULL, NULL, line, -1);
1285 /* Do we start in bold? */
1286 cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1288 k = i + BYTE_VIEW_WIDTH;
1290 /* Print the hex bit */
1293 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1294 line[cur++] = hexchars[pd[i] & 0x0f];
1296 line[cur++] = ' '; line[cur++] = ' ';
1300 /* insert a space every BYTE_VIEW_SEP bytes */
1301 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1302 /* Did we cross a bold/plain boundary? */
1303 new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1304 if (cur_font != new_font) {
1305 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1306 cur_font = new_font;
1311 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1315 /* Print the ASCII bit */
1316 cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1319 if (encoding == CHAR_ASCII) {
1322 else if (encoding == CHAR_EBCDIC) {
1323 c = EBCDIC_to_ASCII1(pd[i]);
1326 g_assert_not_reached();
1328 line[cur++] = isprint(c) ? c : '.';
1333 /* insert a space every BYTE_VIEW_SEP bytes */
1334 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1335 /* Did we cross a bold/plain boundary? */
1336 new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1337 if (cur_font != new_font) {
1338 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, cur);
1339 cur_font = new_font;
1345 gtk_text_insert(bv_text, cur_font, NULL, NULL, line, -1);
1348 if (prefs.gui_hex_dump_highlight_style)
1349 revstyle = "reverse";
1353 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1, "plain",
1355 /* Do we start in reverse? */
1356 reverse = i >= bstart && i < bend;
1358 k = i + BYTE_VIEW_WIDTH;
1360 /* Print the hex bit */
1363 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1364 line[cur++] = hexchars[pd[i] & 0x0f];
1366 line[cur++] = ' '; line[cur++] = ' ';
1369 newreverse = i >= bstart && i < bend;
1370 /* Have we gone from reverse to plain? */
1371 if (reverse && (reverse != newreverse)) {
1372 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1376 /* Inter byte space if not at end of line */
1379 /* insert a space every BYTE_VIEW_SEP bytes */
1380 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1384 /* Have we gone from plain to reversed? */
1385 if (!reverse && (reverse != newreverse)) {
1386 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1388 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1391 reverse = newreverse;
1393 /* Print remaining part of line */
1394 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1395 reverse ? revstyle : "plain",
1398 /* Print some space at the end of the line */
1399 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1400 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1404 /* Print the ASCII bit */
1406 /* Do we start in reverse? */
1407 reverse = i >= bstart && i < bend;
1410 if (encoding == CHAR_ASCII) {
1413 else if (encoding == CHAR_EBCDIC) {
1414 c = EBCDIC_to_ASCII1(pd[i]);
1417 g_assert_not_reached();
1419 line[cur++] = isprint(c) ? c : '.';
1424 newreverse = i >= bstart && i < bend;
1425 /* Have we gone from reverse to plain? */
1426 if (reverse && (reverse != newreverse)) {
1427 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1428 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1430 g_free( (gpointer) convline);
1434 /* insert a space every BYTE_VIEW_SEP bytes */
1435 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1439 /* Have we gone from plain to reversed? */
1440 if (!reverse && (reverse != newreverse)) {
1441 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1442 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1444 g_free( (gpointer) convline);
1447 reverse = newreverse;
1449 /* Print remaining part of line */
1450 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1451 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1452 reverse ? revstyle : "plain",
1454 g_free( (gpointer) convline);
1457 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1462 /* We're done printing the packets; destroy the progress bar if
1464 if (progbar != NULL)
1465 destroy_progress_dlg(progbar);
1467 /* scroll text into position */
1468 #if GTK_MAJOR_VERSION < 2
1469 gtk_text_thaw(bv_text); /* must thaw before adjusting scroll bars */
1474 linenum = bstart / BYTE_VIEW_WIDTH;
1475 scrollval = MIN(linenum * user_font_get_regular_height(),
1476 bv_text->vadj->upper - bv_text->vadj->page_size);
1478 gtk_adjustment_set_value(bv_text->vadj, scrollval);
1482 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1483 gtk_text_buffer_delete_mark(buf, mark);
1489 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1490 field_info *finfo, guint len)
1492 /* do the initial printing and save the information needed */
1493 /* to redraw the display if preferences change. */
1495 int bstart, bend = -1, blen;
1497 if (finfo != NULL) {
1498 bstart = finfo->start;
1499 blen = finfo->length;
1504 if (bstart >= 0 && blen >= 0) {
1505 bend = bstart + blen;
1508 /* save the information needed to redraw the text */
1509 /* should we save the fd & finfo pointers instead ?? */
1510 OBJECT_SET_DATA(bv, E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1511 OBJECT_SET_DATA(bv, E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1512 OBJECT_SET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY,
1513 GINT_TO_POINTER(fd->flags.encoding));
1515 packet_hex_print_common(bv, pd, len, bstart, bend, fd->flags.encoding);
1519 * Redraw the text using the saved information; usually called if
1520 * the preferences have changed.
1523 packet_hex_reprint(GtkWidget *bv)
1525 int start, end, encoding;
1529 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1530 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1531 data = get_byte_view_data_and_length(bv, &len);
1532 g_assert(data != NULL);
1533 encoding = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY));
1535 packet_hex_print_common(bv, data, len, start, end, encoding);
1538 /* List of all protocol tree widgets, so we can globally set the selection
1539 mode and font of all of them. */
1540 static GList *ptree_widgets;
1542 /* Add a protocol tree widget to the list of protocol tree widgets. */
1543 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1546 remember_ptree_widget(GtkWidget *ptreew)
1548 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1550 /* Catch the "destroy" event on the widget, so that we remove it from
1551 the list when it's destroyed. */
1552 SIGNAL_CONNECT(ptreew, "destroy", forget_ptree_widget, NULL);
1555 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1557 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1559 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1562 /* Set the selection mode of a given packet tree window. */
1564 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1566 #if GTK_MAJOR_VERSION >= 2
1567 GtkTreeSelection *selection;
1569 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1571 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1572 I think "browse" in Wireshark makes more sense than "SINGLE" in
1575 #if GTK_MAJOR_VERSION < 2
1576 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1577 GTK_SELECTION_SINGLE);
1579 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1583 #if GTK_MAJOR_VERSION < 2
1584 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1585 GTK_SELECTION_BROWSE);
1587 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1593 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1595 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1598 /* Set the selection mode of all packet tree windows. */
1600 set_ptree_sel_browse_all(gboolean val)
1602 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1605 #if GTK_MAJOR_VERSION < 2
1607 set_ptree_style_cb(gpointer data, gpointer user_data)
1609 gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
1613 set_ptree_font_cb(gpointer data, gpointer user_data)
1615 gtk_widget_modify_font((GtkWidget *)data,
1616 (PangoFontDescription *)user_data);
1621 set_ptree_font_all(FONT_TYPE *font)
1623 #if GTK_MAJOR_VERSION < 2
1626 style = gtk_style_new();
1627 gdk_font_unref(style->font);
1631 g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
1633 /* Now nuke the old style and replace it with the new one. */
1634 gtk_style_unref(item_style);
1637 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1642 gboolean colors_ok = FALSE;
1643 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1644 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1645 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1646 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1648 void proto_draw_colors_init(void)
1654 get_color(&expert_color_chat);
1655 get_color(&expert_color_note);
1656 get_color(&expert_color_warn);
1657 get_color(&expert_color_error);
1663 #if GTK_MAJOR_VERSION >= 2
1664 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1665 GtkCellRenderer *cell,
1666 GtkTreeModel *tree_model,
1672 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1675 proto_draw_colors_init();
1678 /* for the various possible attributes, see:
1679 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1681 * color definitions can be found at:
1682 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1683 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1686 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1687 * weight/style: doesn't take any effect
1690 /* for each field, we have to reset the renderer attributes */
1691 g_object_set (cell, "foreground-set", FALSE, NULL);
1693 g_object_set (cell, "background", "white", NULL);
1694 g_object_set (cell, "background-set", TRUE, NULL);
1696 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1697 g_object_set (cell, "underline-set", FALSE, NULL);
1699 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1700 g_object_set (cell, "style-set", FALSE, NULL);*/
1702 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1703 g_object_set (cell, "weight-set", FALSE, NULL);*/
1705 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1706 /* we use "[...]" to mark generated items, no need to change things here */
1708 /* as some fonts don't support italic, don't use this */
1709 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1710 g_object_set (cell, "style-set", TRUE, NULL);
1712 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1713 g_object_set (cell, "weight-set", TRUE, NULL);*/
1716 if(fi->hfinfo->type == FT_PROTOCOL) {
1717 g_object_set (cell, "background", "gray90", NULL);
1718 g_object_set (cell, "background-set", TRUE, NULL);
1719 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1720 g_object_set (cell, "weight-set", TRUE, NULL);*/
1723 if((fi->hfinfo->type == FT_FRAMENUM) ||
1724 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1725 g_object_set (cell, "foreground", "blue", NULL);
1726 g_object_set (cell, "foreground-set", TRUE, NULL);
1728 g_object_set (cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1729 g_object_set (cell, "underline-set", TRUE, NULL);
1732 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1733 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1735 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1736 g_object_set (cell, "background-set", TRUE, NULL);
1739 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1740 g_object_set (cell, "background-set", TRUE, NULL);
1743 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1744 g_object_set (cell, "background-set", TRUE, NULL);
1747 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1748 g_object_set (cell, "background-set", TRUE, NULL);
1751 g_assert_not_reached();
1758 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1760 GtkWidget *tv_scrollw, *tree_view;
1761 #if GTK_MAJOR_VERSION >= 2
1762 GtkTreeStore *store;
1763 GtkCellRenderer *renderer;
1764 GtkTreeViewColumn *column;
1769 tv_scrollw = scrolled_window_new(NULL, NULL);
1770 #if GTK_MAJOR_VERSION >= 2
1771 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1775 #if GTK_MAJOR_VERSION < 2
1776 tree_view = ctree_new(1, 0);
1777 SIGNAL_CONNECT(tree_view, "key-press-event", toggle_tree, NULL );
1778 SIGNAL_CONNECT(tree_view, "tree-expand", expand_tree, NULL );
1779 SIGNAL_CONNECT(tree_view, "tree-collapse", collapse_tree, NULL );
1780 /* I need this next line to make the widget work correctly with hidden
1781 * column titles and GTK_SELECTION_BROWSE */
1782 gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1784 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1785 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1786 g_object_unref(G_OBJECT(store));
1787 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1788 renderer = gtk_cell_renderer_text_new();
1789 g_object_set (renderer, "ypad", 0, NULL);
1790 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1791 -1, "Name", renderer,
1793 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1795 gtk_tree_view_column_set_cell_data_func(column,
1801 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1802 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1803 SIGNAL_CONNECT(tree_view, "row-expanded", expand_tree, NULL);
1804 SIGNAL_CONNECT(tree_view, "row-collapsed", collapse_tree, NULL);
1806 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1807 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1808 #if GTK_MAJOR_VERSION < 2
1809 if(item_style == NULL) {
1810 item_style = gtk_style_new();
1811 gdk_font_unref(item_style->font);
1812 item_style->font = user_font_get_regular();
1815 gtk_widget_set_style(tree_view, item_style);
1817 gtk_widget_modify_font(tree_view, user_font_get_regular());
1819 remember_ptree_widget(tree_view);
1821 *tree_view_p = tree_view;
1826 #if GTK_MAJOR_VERSION < 2
1827 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1829 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1833 for(i=0; i < num_tree_types; i++) {
1834 tree_is_expanded[i] = TRUE;
1836 #if GTK_MAJOR_VERSION < 2
1837 proto_tree_draw(protocol_tree, tree_view);
1838 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
1840 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1844 #if GTK_MAJOR_VERSION < 2
1845 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1847 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1851 for(i=0; i < num_tree_types; i++) {
1852 tree_is_expanded[i] = FALSE;
1854 #if GTK_MAJOR_VERSION < 2
1855 proto_tree_draw(protocol_tree, tree_view);
1857 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1862 struct proto_tree_draw_info {
1863 #if GTK_MAJOR_VERSION < 2
1865 GtkCTreeNode *ctree_node;
1867 GtkTreeView *tree_view;
1873 main_proto_tree_draw(proto_tree *protocol_tree)
1875 proto_tree_draw(protocol_tree, tree_view);
1880 tree_view_follow_link(field_info *fi)
1884 if(fi->hfinfo->type == FT_FRAMENUM) {
1885 cf_goto_frame(&cfile, fi->value.value.integer);
1887 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1888 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1889 browser_open_url(url);
1895 /* If the user selected a position in the tree view, try to find
1896 * the item in the GUI proto_tree that corresponds to that byte, and
1899 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1901 #if GTK_MAJOR_VERSION < 2
1909 if(gtk_clist_get_selection_info(GTK_CLIST(widget),
1910 (gint) (((GdkEventButton *)event)->x),
1911 (gint) (((GdkEventButton *)event)->y),
1914 ctree = GTK_CTREE(widget);
1916 node = gtk_ctree_node_nth(ctree, row);
1919 gtk_ctree_select(ctree, node);
1921 /* if that's a doubleclick, try to follow the link */
1922 if(event->type == GDK_2BUTTON_PRESS) {
1923 fi = gtk_ctree_node_get_row_data(ctree, node);
1924 tree_view_follow_link(fi);
1930 GtkTreeSelection *sel;
1933 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1934 (gint) (((GdkEventButton *)event)->x),
1935 (gint) (((GdkEventButton *)event)->y),
1936 &path, NULL, NULL, NULL))
1938 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1939 gtk_tree_selection_select_path(sel, path);
1941 /* if that's a doubleclick, try to follow the link */
1942 if(event->type == GDK_2BUTTON_PRESS) {
1943 GtkTreeModel *model;
1947 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1948 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1949 tree_view_follow_link(fi);
1959 /* fill the whole protocol tree with the string values */
1961 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1963 #if GTK_MAJOR_VERSION >= 2
1964 GtkTreeStore *store;
1966 struct proto_tree_draw_info info;
1968 #if GTK_MAJOR_VERSION < 2
1969 info.ctree = GTK_CTREE(tree_view);
1970 info.ctree_node = NULL;
1972 gtk_clist_freeze(GTK_CLIST(tree_view));
1974 info.tree_view = GTK_TREE_VIEW(tree_view);
1977 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1981 * Clear out any crud left over in the display of the protocol
1982 * tree, by removing all nodes from the tree.
1983 * This is how it's done in testgtk.c in GTK+.
1985 #if GTK_MAJOR_VERSION < 2
1986 gtk_clist_clear(GTK_CLIST(tree_view));
1988 gtk_tree_store_clear(store);
1991 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1993 #if GTK_MAJOR_VERSION < 2
1994 gtk_clist_thaw(GTK_CLIST(tree_view));
1999 /* fill a single protocol tree item with the string value */
2001 proto_tree_draw_node(proto_node *node, gpointer data)
2003 struct proto_tree_draw_info info;
2004 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2006 field_info *fi = PITEM_FINFO(node);
2007 gchar label_str[ITEM_LABEL_LENGTH];
2009 gboolean is_leaf, is_expanded;
2010 #if GTK_MAJOR_VERSION < 2
2011 GtkCTreeNode *parent;
2013 GtkTreeStore *store;
2018 if (PROTO_ITEM_IS_HIDDEN(node))
2021 /* was a free format label produced? */
2023 label_ptr = fi->rep->representation;
2025 else { /* no, make a generic label */
2026 label_ptr = label_str;
2027 proto_item_fill_label(fi, label_str);
2030 if (node->first_child != NULL) {
2032 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2033 if (tree_is_expanded[fi->tree_type]) {
2037 is_expanded = FALSE;
2042 is_expanded = FALSE;
2045 if(PROTO_ITEM_IS_GENERATED(node)) {
2046 label_ptr = g_strdup_printf("[%s]", label_ptr);
2049 #if GTK_MAJOR_VERSION < 2
2050 info.ctree = parent_info->ctree;
2051 parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
2052 &label_ptr, 5, NULL, NULL, NULL, NULL,
2053 is_leaf, is_expanded );
2055 gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
2057 info.tree_view = parent_info->tree_view;
2058 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2059 gtk_tree_store_append(store, &iter, parent_info->iter);
2060 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2063 if(PROTO_ITEM_IS_GENERATED(node)) {
2068 #if GTK_MAJOR_VERSION < 2
2069 info.ctree_node = parent;
2073 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2074 #if GTK_MAJOR_VERSION >= 2
2075 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2077 /* #if GTK_MINOR_VERSION >= 2 */
2078 gtk_tree_view_expand_to_path(info.tree_view, path);
2080 gtk_tree_view_expand_row(info.tree_view, path, FALSE);
2083 gtk_tree_view_collapse_row(info.tree_view, path);
2084 gtk_tree_path_free(path);
2090 * Clear the hex dump and protocol tree panes.
2093 clear_tree_and_hex_views(void)
2095 /* Clear the hex dump by getting rid of all the byte views. */
2096 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
2097 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
2099 /* Add a placeholder byte view so that there's at least something
2100 displayed in the byte view notebook. */
2101 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
2103 /* Clear the protocol tree by removing all nodes in the ctree.
2104 This is how it's done in testgtk.c in GTK+ */
2105 #if GTK_MAJOR_VERSION < 2
2106 gtk_clist_clear(GTK_CLIST(tree_view));
2108 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));