2 * Routines for GTK+ packet display
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * Jeff Foster, 2001/03/12, added support for displaying named
11 * data sources as tabbed hex windows
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
43 #include <gdk/gdkkeysyms.h>
47 #include <epan/epan_dissect.h>
49 #include <epan/packet.h>
50 #include <epan/charsets.h>
51 #include <epan/prefs.h>
53 #include "../isprint.h"
54 #include "../alert_box.h"
55 #include "../simple_dialog.h"
56 #include "../progress_dlg.h"
57 #include "../ui_util.h"
58 #include <wsutil/file_util.h>
61 #include "gtk/color_utils.h"
62 #include "gtk/capture_file_dlg.h"
63 #include "gtk/packet_win.h"
64 #include "gtk/file_dlg.h"
65 #include "gtk/gui_utils.h"
66 #include "gtk/gtkglobals.h"
67 #include "gtk/font_utils.h"
68 #include "gtk/webbrowser.h"
70 #include "gtk/main_menu.h"
71 #include "gtk/main_proto_draw.h"
74 #include <gdk/gdkwin32.h>
76 #include "file_dlg_win32.h"
80 #define BYTE_VIEW_WIDTH 16
81 #define BYTE_VIEW_SEP 8
83 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
84 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
85 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
86 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
87 #define E_BYTE_VIEW_START_KEY "byte_view_start"
88 #define E_BYTE_VIEW_END_KEY "byte_view_end"
89 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
90 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
91 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
94 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
95 proto_tree *tree, GtkWidget *tree_view);
98 proto_tree_draw_node(proto_node *node, gpointer data);
100 /* Get the current text window for the notebook. */
102 get_notebook_bv_ptr(GtkWidget *nb_ptr)
107 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
108 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
110 return GTK_BIN(bv_page)->child;
116 * Get the data and length for a byte view, given the byte view page.
117 * Return the pointer, or NULL on error, and set "*data_len" to the length.
120 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
122 tvbuff_t *byte_view_tvb;
123 const guint8 *data_ptr;
125 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
126 if (byte_view_tvb == NULL)
129 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
130 *data_len = tvb_length(byte_view_tvb);
135 * Set the current text window for the notebook to the window that
136 * refers to a particular tvbuff.
139 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
142 GtkWidget *bv_page, *bv;
146 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
148 bv = GTK_BIN(bv_page)->child;
149 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
152 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
158 /* Redraw a given byte view window. */
160 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
166 bv = get_notebook_bv_ptr(nb);
168 data = get_byte_view_data_and_length(bv, &len);
170 packet_hex_print(bv, data, fd, finfo, len);
174 /* Redraw all byte view windows. */
176 redraw_hex_dump_all(void)
178 if (cfile.current_frame != NULL)
179 redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
181 redraw_hex_dump_packet_wins();
183 /* XXX - this is a hack, to workaround a bug in GTK2.x!
184 when changing the font size, even refilling of the corresponding
185 gtk_text_buffer doesn't seem to trigger an update.
186 The only workaround is to freshly select the frame, which will remove any
187 existing notebook tabs and "restart" the whole byte view again. */
188 if (cfile.current_frame != NULL) {
189 cfile.current_row = -1;
190 cf_goto_frame(&cfile, cfile.current_frame->num);
195 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
196 GtkTreePath *path _U_, gpointer user_data _U_)
201 model = gtk_tree_view_get_model(tree_view);
202 gtk_tree_model_get(model, iter, 1, &finfo, -1);
206 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
207 * are thus presumably leaf nodes and cannot be expanded.
209 if (finfo->tree_type != -1) {
210 g_assert(finfo->tree_type >= 0 &&
211 finfo->tree_type < num_tree_types);
212 tree_is_expanded[finfo->tree_type] = TRUE;
217 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
218 GtkTreePath *path _U_, gpointer user_data _U_)
223 model = gtk_tree_view_get_model(tree_view);
224 gtk_tree_model_get(model, iter, 1, &finfo, -1);
228 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
229 * are thus presumably leaf nodes and cannot be collapsed.
231 if (finfo->tree_type != -1) {
232 g_assert(finfo->tree_type >= 0 &&
233 finfo->tree_type < num_tree_types);
234 tree_is_expanded[finfo->tree_type] = FALSE;
238 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
239 #define BYTES_PER_LINE 16 /* max byte values in a line */
240 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
241 /* max number of characters hex dump takes -
242 2 digits plus trailing blank
243 plus separator between first and
245 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
246 /* number of characters those bytes take;
247 3 characters per byte of hex dump,
248 2 blanks separating hex from ASCII,
249 1 character per byte of ASCII dump */
250 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
251 /* number of characters per line;
252 offset, 2 blanks separating offset
253 from data dump, data dump */
254 #define MAX_LINES 100
255 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
257 /* Which byte the offset is referring to. Associates
258 * whitespace with the preceding digits. */
260 byte_num(int offset, int start_point)
262 return (offset - start_point) / 3;
265 struct field_lookup_info {
271 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
275 struct field_lookup_info *fli = (struct field_lookup_info *)data;
277 gtk_tree_model_get(model, iter, 1, &fi, -1);
285 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
287 struct field_lookup_info fli;
289 g_assert(finfo != NULL);
291 model = gtk_tree_view_get_model(tree_view);
293 gtk_tree_model_foreach(model, lookup_finfo, &fli);
295 return gtk_tree_model_get_path(model, &fli.iter);
298 /* If the user selected a certain byte in the byte view, try to find
299 * the item in the GUI proto_tree that corresponds to that byte, and:
301 * if we succeed, select it, and return TRUE;
302 * if we fail, return FALSE. */
304 byte_view_select(GtkWidget *widget, GdkEventButton *event)
307 GtkTreeView *tree_view;
308 GtkTextView *bv = GTK_TEXT_VIEW(widget);
325 * Get the number of digits of offset being displayed, and
326 * compute the columns of various parts of the display.
328 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
331 * The column of the first hex digit in the first half.
332 * That starts after "ndigits" digits of offset and two
335 digits_start_1 = ndigits + 2;
338 * The column of the last hex digit in the first half.
339 * There are BYTES_PER_LINE/2 bytes displayed in the first
340 * half; there are 2 characters per byte, plus a separating
341 * blank after all but the last byte's characters.
343 * Then subtract 1 to get the last column of the first half
344 * rather than the first column after the first half.
346 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
347 (BYTES_PER_LINE/2 - 1) - 1;
350 * The column of the first hex digit in the second half.
351 * Add back the 1 to get the first column after the first
352 * half, and then add 2 for the 2 separating blanks between
355 digits_start_2 = digits_end_1 + 3;
358 * The column of the last hex digit in the second half.
359 * Add the same value we used to get "digits_end_1" from
362 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
363 (BYTES_PER_LINE/2 - 1) - 1;
366 * The column of the first "text dump" character in the first half.
367 * Add back the 1 to get the first column after the second
368 * half's hex dump, and then add 3 for the 3 separating blanks
369 * between the hex and text dummp.
371 text_start_1 = digits_end_2 + 4;
374 * The column of the last "text dump" character in the first half.
375 * There are BYTES_PER_LINE/2 bytes displayed in the first
376 * half; there is 1 character per byte.
378 * Then subtract 1 to get the last column of the first half
379 * rather than the first column after the first half.
381 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
384 * The column of the first "text dump" character in the second half.
385 * Add back the 1 to get the first column after the first half,
386 * and then add 1 for the separating blank between the halves.
388 text_start_2 = text_end_1 + 2;
391 * The column of the last "text dump" character in second half.
392 * Add the same value we used to get "text_end_1" from
395 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
397 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
400 * Somebody clicked on the dummy byte view; do nothing.
404 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
405 E_BYTE_VIEW_TREE_VIEW_PTR));
407 /* get the row/column selected */
408 gtk_text_view_window_to_buffer_coords(bv,
409 gtk_text_view_get_window_type(bv, event->window),
410 (gint) event->x, (gint) event->y, &x, &y);
411 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
412 row = gtk_text_iter_get_line(&iter);
413 column = gtk_text_iter_get_line_offset(&iter);
415 /* Given the column and row, determine which byte offset
416 * the user clicked on. */
417 if (column >= digits_start_1 && column <= digits_end_1) {
418 byte = byte_num(column, digits_start_1);
423 else if (column >= digits_start_2 && column <= digits_end_2) {
424 byte = byte_num(column, digits_start_2);
430 else if (column >= text_start_1 && column <= text_end_1) {
431 byte = column - text_start_1;
433 else if (column >= text_start_2 && column <= text_end_2) {
434 byte = 8 + column - text_start_2;
437 /* The user didn't select a hex digit or
438 * text-dump character. */
442 /* Add the number of bytes from the previous rows. */
445 /* Get the data source tvbuff */
446 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
448 return highlight_field(tvb, byte, tree_view, tree);
451 /* This highlights the field in the proto tree that is at position byte */
453 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
457 GtkTreePath *first_path, *path;
459 struct field_lookup_info fli;
462 /* Find the finfo that corresponds to our byte. */
463 finfo = proto_find_field_from_offset(tree, byte, tvb);
469 model = gtk_tree_view_get_model(tree_view);
471 gtk_tree_model_foreach(model, lookup_finfo, &fli);
473 /* Expand our field's row */
474 first_path = gtk_tree_model_get_path(model, &fli.iter);
475 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
476 expand_tree(tree_view, &fli.iter, NULL, NULL);
478 /* ... and its parents */
479 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
480 path = gtk_tree_model_get_path(model, &parent);
481 gtk_tree_view_expand_row(tree_view, path, FALSE);
482 expand_tree(tree_view, &parent, NULL, NULL);
484 gtk_tree_path_free(path);
487 /* select our field's row */
488 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
491 /* And position the window so the selection is visible.
492 * Position the selection in the middle of the viewable
494 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
496 gtk_tree_path_free(first_path);
501 /* Calls functions for different mouse-button presses. */
503 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
505 GdkEventButton *event_button = NULL;
507 if(widget == NULL || event == NULL || data == NULL) {
511 if(event->type == GDK_BUTTON_PRESS) {
512 event_button = (GdkEventButton *) event;
514 /* To qoute the "Gdk Event Structures" doc:
515 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
516 switch(event_button->button) {
519 return byte_view_select(widget, event_button);
521 return popup_menu_handler(widget, event, data);
535 byte_nb = gtk_notebook_new();
536 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
538 /* this will only have an effect, if no tabs are shown */
539 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
541 /* set the tabs scrollable, if they don't fit into the pane */
542 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
544 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
545 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
547 /* Add a placeholder byte view so that there's at least something
548 displayed in the byte view notebook. */
549 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
555 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
557 const guint8 *byte_data;
560 byte_data = get_byte_view_data_and_length(bv, &byte_len);
561 if (byte_data == NULL) {
562 /* This must be the dummy byte view if no packet is selected. */
565 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
569 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
570 proto_tree *tree, GtkWidget *tree_view)
572 GtkWidget *byte_view, *byte_scrollw, *label;
576 /* Byte view. Create a scrolled window for the text. */
577 byte_scrollw = scrolled_window_new(NULL, NULL);
578 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
580 /* Add scrolled pane to tabbed window */
581 label = gtk_label_new(name);
582 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
584 gtk_widget_show(byte_scrollw);
586 byte_view = gtk_text_view_new();
587 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
588 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
589 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
590 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
591 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
592 gtk_text_buffer_create_tag(buf, "reverse",
593 "font-desc", user_font_get_regular(),
594 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
595 "background-gdk", &style->base[GTK_STATE_SELECTED],
597 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
598 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
599 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
601 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
602 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
603 g_object_get_data(G_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
605 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
606 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
608 gtk_widget_show(byte_view);
610 /* no tabs if this is the first page */
611 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
612 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
614 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
616 /* set this page (this will print the packet data) */
617 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
618 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
624 add_main_byte_views(epan_dissect_t *edt)
626 add_byte_views(edt, tree_view, byte_nb_ptr);
630 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
631 GtkWidget *byte_nb_ptr)
637 * Get rid of all the old notebook tabs.
639 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
640 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
643 * Add to the specified byte view notebook tabs for hex dumps
644 * of all the data sources for the specified frame.
646 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
648 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
653 * Initially select the first byte view.
655 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
660 static GtkWidget *savehex_dlg=NULL;
663 savehex_dlg_destroy_cb(void)
669 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
671 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
673 gboolean end_of_line = TRUE; /* Initial state is end of line */
674 int byte_line_part_length;
679 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
680 hex_str = g_string_new("");
681 char_str= g_string_new("");
686 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
689 g_string_append_printf(hex_str," %02x",*data_p);
691 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
696 /* Look ahead to see if this is the end of the data */
697 byte_line_part_length = (++i) % byte_line_length;
699 /* End of data - need to fill in spaces in hex string and then do "end of line".
702 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
703 g_string_append(hex_str," "); /* Three spaces for each missing byte */
707 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
713 g_string_append(copy_buffer, hex_str->str);
715 /* Two spaces between hex and text */
716 g_string_append_c(copy_buffer, ' ');
717 g_string_append_c(copy_buffer, ' ');
718 g_string_append(copy_buffer, char_str->str);
720 /* Setup ready for next line */
721 g_string_assign(char_str,"");
722 g_string_assign(hex_str, "\n");
726 g_string_free(hex_str, TRUE);
727 g_string_free(char_str, TRUE);
731 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
736 if(isprint(*data_p)) {
738 } else if(*data_p==0x0a) {
739 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
741 return 1; /* Just ignore non-printable bytes */
743 g_string_append_c(copy_buffer,to_append);
748 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
750 g_string_append_printf(copy_buffer, "%02x", *data_p);
755 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
760 int bytes_consumed = 0;
763 const guint8* data_p;
765 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
767 bv = get_notebook_bv_ptr(byte_nb_ptr);
769 /* shouldn't happen */
770 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
774 data_p = get_byte_view_data_and_length(bv, &len);
775 g_assert(data_p != NULL);
777 flags = data_type & CD_FLAGSMASK;
778 data_type = data_type & CD_TYPEMASK;
780 if(flags & CD_FLAGS_SELECTEDONLY) {
783 /* Get the start and end of the highlighted bytes.
784 * XXX The keys appear to be REVERSED start <-> end throughout this file!
785 * Should this be fixed? There is one exception - packet_hex_reprint,
786 * so can't just change it round without functional change.
788 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
789 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
791 if(start >= 0 && end > start && (end - start <= (int)len)) {
799 /* This is too different from other text formats - handle separately */
800 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
803 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
804 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
807 /* Completely different logic to text copies - leave copy buffer alone */
808 copy_binary_to_clipboard(data_p,len);
811 /* Incrementally write to text buffer in various formats */
815 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
818 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
821 g_assert_not_reached();
825 g_assert(bytes_consumed>0);
826 data_p += bytes_consumed;
827 len -= bytes_consumed;
832 if(copy_buffer->len > 0) {
833 copy_to_clipboard(copy_buffer);
836 g_string_free(copy_buffer, TRUE);
839 /* save the current highlighted hex data */
841 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
846 const guint8 *data_p = NULL;
847 const char *file = NULL;
849 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
851 if (!file ||! *file) {
852 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
856 /* Must check if file name exists first */
858 bv = get_notebook_bv_ptr(byte_nb_ptr);
860 /* shouldn't happen */
861 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
865 * Retrieve the info we need
867 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
868 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
869 data_p = get_byte_view_data_and_length(bv, &len);
871 if (data_p == NULL || start == -1 || start > end) {
872 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
873 "No data selected to save!");
877 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
879 open_failure_alert_box(file, errno, TRUE);
882 if (ws_write(fd, data_p + start, end - start) < 0) {
883 write_failure_alert_box(file, errno);
887 if (ws_close(fd) < 0) {
888 write_failure_alert_box(file, errno);
892 /* Get rid of the dialog box */
893 window_destroy(GTK_WIDGET(savehex_dlg));
896 /* Launch the dialog box to put up the file selection box etc */
897 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
901 const guint8 *data_p = NULL;
908 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
912 /* don't show up the dialog, if no data has to be saved */
913 bv = get_notebook_bv_ptr(byte_nb_ptr);
915 /* shouldn't happen */
916 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
919 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
920 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
921 data_p = get_byte_view_data_and_length(bv, &len);
923 if (data_p == NULL || start == -1 || start > end) {
924 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
928 /* if the window is already open, bring it to front */
930 reactivate_window(savehex_dlg);
935 * Build the dialog box we need.
937 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
940 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
941 end - start, plurality(end - start, "byte", "bytes"));
942 dlg_lb = gtk_label_new(label);
944 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
945 gtk_widget_show(dlg_lb);
947 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
949 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
950 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
952 window_destroy(savehex_dlg);
958 /* Update the progress bar this many times when reading a file. */
959 #define N_PROGBAR_UPDATES 100
963 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
964 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
965 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
966 * presumably why there's a progress bar for it.)
968 * Perhaps what's needed is a custom widget (either one that lets you stuff
969 * text into it more quickly, or one that's a "virtual" widget so that the
970 * text for a row is constructed, via a callback, when the row is to be
971 * displayed). A custom widget might also let us treat the offset, hex
972 * data, and ASCII data as three columns, so you can select purely in
973 * the hex dump column.
976 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
977 int bend, int astart, int aend, int encoding)
979 int i = 0, j, k, cur;
980 guchar line[MAX_LINES_LEN + 1];
981 static guchar hexchars[16] = {
982 '0', '1', '2', '3', '4', '5', '6', '7',
983 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
985 unsigned int use_digits;
986 gboolean reverse, newreverse;
987 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
988 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
990 const char *revstyle;
991 GtkTextMark *mark = NULL;
993 progdlg_t *progbar = NULL;
995 gboolean progbar_stop_flag;
996 GTimeVal progbar_start_time;
997 gchar progbar_status_str[100];
998 int progbar_nextstep;
1001 gtk_text_buffer_set_text(buf, "", 0);
1002 gtk_text_buffer_get_start_iter(buf, &iter);
1004 gtk_text_view_set_buffer( bv_text_view, NULL);
1007 * How many of the leading digits of the offset will we supply?
1008 * We always supply at least 4 digits, but if the maximum offset
1009 * won't fit in 4 digits, we use as many digits as will be needed.
1011 if (((len - 1) & 0xF0000000) != 0)
1012 use_digits = 8; /* need all 8 digits */
1013 else if (((len - 1) & 0x0F000000) != 0)
1014 use_digits = 7; /* need 7 digits */
1015 else if (((len - 1) & 0x00F00000) != 0)
1016 use_digits = 6; /* need 6 digits */
1017 else if (((len - 1) & 0x000F0000) != 0)
1018 use_digits = 5; /* need 5 digits */
1020 use_digits = 4; /* we'll supply 4 digits */
1022 /* Record the number of digits in this text view. */
1023 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1025 /* Update the progress bar when it gets to this value. */
1026 progbar_nextstep = 0;
1027 /* When we reach the value that triggers a progress bar update,
1028 bump that value by this amount. */
1029 progbar_quantum = len/N_PROGBAR_UPDATES;
1030 /* Progress so far. */
1033 progbar_stop_flag = FALSE;
1034 g_get_current_time(&progbar_start_time);
1038 /* Create the progress bar if necessary.
1039 We check on every iteration of the loop, so that it takes no
1040 longer than the standard time to create it (otherwise, for a
1041 large packet, we might take considerably longer than that standard
1042 time in order to get to the next progress bar step). */
1043 if (progbar == NULL)
1044 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1047 &progbar_start_time,
1050 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1051 when we update it, we have to run the GTK+ main loop to get it
1052 to repaint what's pending, and doing so may involve an "ioctl()"
1053 to see if there's any pending input from an X server, and doing
1054 that for every packet can be costly, especially on a big file. */
1055 if (i >= progbar_nextstep) {
1056 /* let's not divide by zero. I should never be started
1057 * with count == 0, so let's assert that
1060 progbar_val = (gfloat) i / len;
1062 if (progbar != NULL) {
1063 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1064 "%4u of %u bytes", i, len);
1065 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1068 progbar_nextstep += progbar_quantum;
1071 if (progbar_stop_flag) {
1072 /* Well, the user decided to abort the operation. Just stop,
1073 and arrange to return TRUE to our caller, so they know it
1074 was stopped explicitly. */
1078 /* Print the line number */
1082 c = (i >> (j*4)) & 0xF;
1083 line[cur++] = hexchars[c];
1088 /* Display with inverse video ? */
1089 if (prefs.gui_hex_dump_highlight_style)
1090 revstyle = "reverse";
1094 /* Do we start in reverse? */
1095 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1097 k = i + BYTE_VIEW_WIDTH;
1099 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1103 /* Print the hex bit */
1106 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1107 line[cur++] = hexchars[pd[i] & 0x0f];
1109 line[cur++] = ' '; line[cur++] = ' ';
1112 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1113 /* Have we gone from reverse to plain? */
1114 if (reverse && (reverse != newreverse)) {
1115 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1119 /* Inter byte space if not at end of line */
1122 /* insert a space every BYTE_VIEW_SEP bytes */
1123 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1127 /* Have we gone from plain to reversed? */
1128 if (!reverse && (reverse != newreverse)) {
1129 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1131 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1134 reverse = newreverse;
1137 /* Print remaining part of line */
1138 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1143 /* Print some space at the end of the line */
1144 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1146 /* Print the ASCII bit */
1148 /* Do we start in reverse? */
1149 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1151 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1158 if (encoding == CHAR_ASCII) {
1161 else if (encoding == CHAR_EBCDIC) {
1162 c = EBCDIC_to_ASCII1(pd[i]);
1165 g_assert_not_reached();
1167 line[cur++] = isprint(c) ? c : '.';
1172 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1173 /* Have we gone from reverse to plain? */
1174 if (reverse && (reverse != newreverse)) {
1175 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1181 /* insert a space every BYTE_VIEW_SEP bytes */
1182 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1186 /* Have we gone from plain to reversed? */
1187 if (!reverse && (reverse != newreverse)) {
1188 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1192 reverse = newreverse;
1194 /* Print remaining part of line */
1196 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1201 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1202 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1208 /* We're done printing the packets; destroy the progress bar if
1210 if (progbar != NULL)
1211 destroy_progress_dlg(progbar);
1213 /* scroll text into position */
1215 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1218 gtk_text_view_set_buffer( bv_text_view, buf);
1221 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1222 gtk_text_buffer_delete_mark(buf, mark);
1224 g_object_unref(buf);
1228 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1229 field_info *finfo, guint len)
1231 /* do the initial printing and save the information needed */
1232 /* to redraw the display if preferences change. */
1234 int bstart = -1, bend = -1, blen = -1;
1235 int astart = -1, aend = -1, alen = -1;
1237 if (finfo != NULL) {
1238 bstart = finfo->start;
1239 blen = finfo->length;
1240 astart = finfo->appendix_start;
1241 alen = finfo->appendix_length;
1243 if (bstart >= 0 && blen >= 0) {
1244 bend = bstart + blen;
1246 if (astart >= 0 && alen >= 0) {
1247 aend = astart + alen;
1250 /* save the information needed to redraw the text */
1251 /* should we save the fd & finfo pointers instead ?? */
1252 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1253 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1254 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1255 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1256 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1257 GUINT_TO_POINTER((guint)fd->flags.encoding));
1259 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1263 * Redraw the text using the saved information; usually called if
1264 * the preferences have changed.
1267 packet_hex_reprint(GtkWidget *bv)
1269 int start, end, encoding;
1274 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1275 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1276 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1277 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1278 data = get_byte_view_data_and_length(bv, &len);
1279 g_assert(data != NULL);
1280 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1282 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1285 /* List of all protocol tree widgets, so we can globally set the selection
1286 mode and font of all of them. */
1287 static GList *ptree_widgets;
1289 /* Add a protocol tree widget to the list of protocol tree widgets. */
1290 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1293 remember_ptree_widget(GtkWidget *ptreew)
1295 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1297 /* Catch the "destroy" event on the widget, so that we remove it from
1298 the list when it's destroyed. */
1299 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1302 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1304 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1306 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1309 /* Set the selection mode of a given packet tree window. */
1311 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1313 GtkTreeSelection *selection;
1315 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1316 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1317 I think "browse" in Wireshark makes more sense than "SINGLE" in
1320 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1323 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1328 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1330 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1333 /* Set the selection mode of all packet tree windows. */
1335 set_ptree_sel_browse_all(gboolean val)
1337 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1341 set_ptree_font_cb(gpointer data, gpointer user_data)
1343 gtk_widget_modify_font((GtkWidget *)data,
1344 (PangoFontDescription *)user_data);
1348 set_ptree_font_all(PangoFontDescription *font)
1350 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1354 gboolean colors_ok = FALSE;
1355 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1356 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1357 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1358 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1359 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1360 GdkColor hidden_proto_item = { 0, 0x4400, 0x4400, 0x4400 }; /* gray */
1362 void proto_draw_colors_init(void)
1368 get_color(&expert_color_chat);
1369 get_color(&expert_color_note);
1370 get_color(&expert_color_warn);
1371 get_color(&expert_color_error);
1372 get_color(&expert_color_foreground);
1373 get_color(&hidden_proto_item);
1379 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1380 GtkCellRenderer *cell,
1381 GtkTreeModel *tree_model,
1387 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1390 proto_draw_colors_init();
1393 /* for the various possible attributes, see:
1394 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1396 * color definitions can be found at:
1397 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1398 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1401 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1402 * weight/style: doesn't take any effect
1405 /* for each field, we have to reset the renderer attributes */
1406 g_object_set (cell, "foreground-set", FALSE, NULL);
1408 g_object_set (cell, "background-set", FALSE, NULL);
1410 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1411 g_object_set (cell, "underline-set", FALSE, NULL);
1413 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1414 g_object_set (cell, "style-set", FALSE, NULL);*/
1416 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1417 g_object_set (cell, "weight-set", FALSE, NULL);*/
1419 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1420 /* we use "[...]" to mark generated items, no need to change things here */
1422 /* as some fonts don't support italic, don't use this */
1423 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1424 g_object_set (cell, "style-set", TRUE, NULL);
1426 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1427 g_object_set (cell, "weight-set", TRUE, NULL);*/
1430 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1431 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1432 g_object_set (cell, "foreground-set", TRUE, NULL);
1435 if(fi->hfinfo->type == FT_PROTOCOL) {
1436 g_object_set (cell, "background", "gray90", NULL);
1437 g_object_set (cell, "background-set", TRUE, NULL);
1438 g_object_set (cell, "foreground", "black", NULL);
1439 g_object_set (cell, "foreground-set", TRUE, NULL);
1440 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1441 g_object_set (cell, "weight-set", TRUE, NULL);*/
1444 if((fi->hfinfo->type == FT_FRAMENUM) ||
1445 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1446 render_as_url(cell);
1449 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1450 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1452 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1453 g_object_set (cell, "background-set", TRUE, NULL);
1456 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1457 g_object_set (cell, "background-set", TRUE, NULL);
1460 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1461 g_object_set (cell, "background-set", TRUE, NULL);
1464 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1465 g_object_set (cell, "background-set", TRUE, NULL);
1468 g_assert_not_reached();
1470 g_object_set (cell, "foreground", "black", NULL);
1471 g_object_set (cell, "foreground-set", TRUE, NULL);
1476 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1478 GtkWidget *tv_scrollw, *tree_view;
1479 GtkTreeStore *store;
1480 GtkCellRenderer *renderer;
1481 GtkTreeViewColumn *column;
1485 tv_scrollw = scrolled_window_new(NULL, NULL);
1486 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1489 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1490 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1491 g_object_unref(G_OBJECT(store));
1492 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1493 renderer = gtk_cell_renderer_text_new();
1494 g_object_set (renderer, "ypad", 0, NULL);
1495 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1496 -1, "Name", renderer,
1498 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1500 gtk_tree_view_column_set_cell_data_func(column,
1506 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1507 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1508 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1509 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1510 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1511 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1512 gtk_widget_modify_font(tree_view, user_font_get_regular());
1513 remember_ptree_widget(tree_view);
1515 *tree_view_p = tree_view;
1520 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1523 for(i=0; i < num_tree_types; i++) {
1524 tree_is_expanded[i] = TRUE;
1526 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1529 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1532 for(i=0; i < num_tree_types; i++) {
1533 tree_is_expanded[i] = FALSE;
1535 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1539 struct proto_tree_draw_info {
1540 GtkTreeView *tree_view;
1545 main_proto_tree_draw(proto_tree *protocol_tree)
1547 proto_tree_draw(protocol_tree, tree_view);
1552 tree_view_follow_link(field_info *fi)
1556 if(fi->hfinfo->type == FT_FRAMENUM) {
1557 cf_goto_frame(&cfile, fi->value.value.uinteger);
1559 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1560 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1561 browser_open_url(url);
1567 /* If the user selected a position in the tree view, try to find
1568 * the item in the GUI proto_tree that corresponds to that byte, and
1571 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1573 GtkTreeSelection *sel;
1576 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1577 (gint) (((GdkEventButton *)event)->x),
1578 (gint) (((GdkEventButton *)event)->y),
1579 &path, NULL, NULL, NULL))
1581 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1583 /* if that's a doubleclick, try to follow the link */
1584 if(event->type == GDK_2BUTTON_PRESS) {
1585 GtkTreeModel *model;
1589 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1590 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1591 tree_view_follow_link(fi);
1594 else if (((GdkEventButton *)event)->button != 1) {
1595 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1596 gtk_tree_selection_select_path(sel, path);
1604 /* fill the whole protocol tree with the string values */
1606 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1608 GtkTreeStore *store;
1609 struct proto_tree_draw_info info;
1611 info.tree_view = GTK_TREE_VIEW(tree_view);
1614 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1617 * Clear out any crud left over in the display of the protocol
1618 * tree, by removing all nodes from the tree.
1619 * This is how it's done in testgtk.c in GTK+.
1621 gtk_tree_store_clear(store);
1623 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1627 /* fill a single protocol tree item with the string value */
1629 proto_tree_draw_node(proto_node *node, gpointer data)
1631 struct proto_tree_draw_info info;
1632 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1634 field_info *fi = PITEM_FINFO(node);
1635 gchar label_str[ITEM_LABEL_LENGTH];
1637 gboolean is_leaf, is_expanded;
1638 GtkTreeStore *store;
1642 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
1645 /* was a free format label produced? */
1647 label_ptr = fi->rep->representation;
1649 else { /* no, make a generic label */
1650 label_ptr = label_str;
1651 proto_item_fill_label(fi, label_str);
1654 if (node->first_child != NULL) {
1656 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1657 if (tree_is_expanded[fi->tree_type]) {
1661 is_expanded = FALSE;
1666 is_expanded = FALSE;
1669 if (PROTO_ITEM_IS_GENERATED(node)) {
1670 if (PROTO_ITEM_IS_HIDDEN(node)) {
1671 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
1673 label_ptr = g_strdup_printf("[%s]", label_ptr);
1675 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
1676 label_ptr = g_strdup_printf("<%s>", label_ptr);
1679 info.tree_view = parent_info->tree_view;
1680 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1681 gtk_tree_store_append(store, &iter, parent_info->iter);
1682 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1684 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
1690 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1691 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1693 gtk_tree_view_expand_to_path(info.tree_view, path);
1695 gtk_tree_view_collapse_row(info.tree_view, path);
1696 gtk_tree_path_free(path);
1701 * Clear the hex dump and protocol tree panes.
1704 clear_tree_and_hex_views(void)
1706 /* Clear the hex dump by getting rid of all the byte views. */
1707 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1708 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1710 /* Add a placeholder byte view so that there's at least something
1711 displayed in the byte view notebook. */
1712 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1714 /* Clear the protocol tree by removing all nodes in the ctree.
1715 This is how it's done in testgtk.c in GTK+ */
1716 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));