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 "wiretap/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_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 cf_goto_frame(&cfile, cfile.current_frame->num);
193 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
194 GtkTreePath *path _U_, gpointer user_data _U_)
199 model = gtk_tree_view_get_model(tree_view);
200 gtk_tree_model_get(model, iter, 1, &finfo, -1);
204 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
205 * are thus presumably leaf nodes and cannot be expanded.
207 if (finfo->tree_type != -1) {
208 g_assert(finfo->tree_type >= 0 &&
209 finfo->tree_type < num_tree_types);
210 tree_is_expanded[finfo->tree_type] = TRUE;
215 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
216 GtkTreePath *path _U_, gpointer user_data _U_)
221 model = gtk_tree_view_get_model(tree_view);
222 gtk_tree_model_get(model, iter, 1, &finfo, -1);
226 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
227 * are thus presumably leaf nodes and cannot be collapsed.
229 if (finfo->tree_type != -1) {
230 g_assert(finfo->tree_type >= 0 &&
231 finfo->tree_type < num_tree_types);
232 tree_is_expanded[finfo->tree_type] = FALSE;
236 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
237 #define BYTES_PER_LINE 16 /* max byte values in a line */
238 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
239 /* max number of characters hex dump takes -
240 2 digits plus trailing blank
241 plus separator between first and
243 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
244 /* number of characters those bytes take;
245 3 characters per byte of hex dump,
246 2 blanks separating hex from ASCII,
247 1 character per byte of ASCII dump */
248 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
249 /* number of characters per line;
250 offset, 2 blanks separating offset
251 from data dump, data dump */
252 #define MAX_LINES 100
253 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
255 /* Which byte the offset is referring to. Associates
256 * whitespace with the preceding digits. */
258 byte_num(int offset, int start_point)
260 return (offset - start_point) / 3;
263 struct field_lookup_info {
269 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
273 struct field_lookup_info *fli = (struct field_lookup_info *)data;
275 gtk_tree_model_get(model, iter, 1, &fi, -1);
283 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
285 struct field_lookup_info fli;
287 g_assert(finfo != NULL);
289 model = gtk_tree_view_get_model(tree_view);
291 gtk_tree_model_foreach(model, lookup_finfo, &fli);
293 return gtk_tree_model_get_path(model, &fli.iter);
296 /* If the user selected a certain byte in the byte view, try to find
297 * the item in the GUI proto_tree that corresponds to that byte, and:
299 * if we succeed, select it, and return TRUE;
300 * if we fail, return FALSE. */
302 byte_view_select(GtkWidget *widget, GdkEventButton *event)
305 GtkTreeView *tree_view;
306 GtkTextView *bv = GTK_TEXT_VIEW(widget);
323 * Get the number of digits of offset being displayed, and
324 * compute the columns of various parts of the display.
326 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
329 * The column of the first hex digit in the first half.
330 * That starts after "ndigits" digits of offset and two
333 digits_start_1 = ndigits + 2;
336 * The column of the last hex digit in the first half.
337 * There are BYTES_PER_LINE/2 bytes displayed in the first
338 * half; there are 2 characters per byte, plus a separating
339 * blank after all but the last byte's characters.
341 * Then subtract 1 to get the last column of the first half
342 * rather than the first column after the first half.
344 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
345 (BYTES_PER_LINE/2 - 1) - 1;
348 * The column of the first hex digit in the second half.
349 * Add back the 1 to get the first column after the first
350 * half, and then add 2 for the 2 separating blanks between
353 digits_start_2 = digits_end_1 + 3;
356 * The column of the last hex digit in the second half.
357 * Add the same value we used to get "digits_end_1" from
360 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
361 (BYTES_PER_LINE/2 - 1) - 1;
364 * The column of the first "text dump" character in the first half.
365 * Add back the 1 to get the first column after the second
366 * half's hex dump, and then add 3 for the 3 separating blanks
367 * between the hex and text dummp.
369 text_start_1 = digits_end_2 + 4;
372 * The column of the last "text dump" character in the first half.
373 * There are BYTES_PER_LINE/2 bytes displayed in the first
374 * half; there is 1 character per byte.
376 * Then subtract 1 to get the last column of the first half
377 * rather than the first column after the first half.
379 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
382 * The column of the first "text dump" character in the second half.
383 * Add back the 1 to get the first column after the first half,
384 * and then add 1 for the separating blank between the halves.
386 text_start_2 = text_end_1 + 2;
389 * The column of the last "text dump" character in second half.
390 * Add the same value we used to get "text_end_1" from
393 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
395 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
398 * Somebody clicked on the dummy byte view; do nothing.
402 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
403 E_BYTE_VIEW_TREE_VIEW_PTR));
405 /* get the row/column selected */
406 gtk_text_view_window_to_buffer_coords(bv,
407 gtk_text_view_get_window_type(bv, event->window),
408 (gint) event->x, (gint) event->y, &x, &y);
409 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
410 row = gtk_text_iter_get_line(&iter);
411 column = gtk_text_iter_get_line_offset(&iter);
413 /* Given the column and row, determine which byte offset
414 * the user clicked on. */
415 if (column >= digits_start_1 && column <= digits_end_1) {
416 byte = byte_num(column, digits_start_1);
421 else if (column >= digits_start_2 && column <= digits_end_2) {
422 byte = byte_num(column, digits_start_2);
428 else if (column >= text_start_1 && column <= text_end_1) {
429 byte = column - text_start_1;
431 else if (column >= text_start_2 && column <= text_end_2) {
432 byte = 8 + column - text_start_2;
435 /* The user didn't select a hex digit or
436 * text-dump character. */
440 /* Add the number of bytes from the previous rows. */
443 /* Get the data source tvbuff */
444 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
446 return highlight_field(tvb, byte, tree_view, tree);
449 /* This highlights the field in the proto tree that is at position byte */
451 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
455 GtkTreePath *first_path, *path;
457 struct field_lookup_info fli;
460 /* Find the finfo that corresponds to our byte. */
461 finfo = proto_find_field_from_offset(tree, byte, tvb);
467 model = gtk_tree_view_get_model(tree_view);
469 gtk_tree_model_foreach(model, lookup_finfo, &fli);
471 /* Expand our field's row */
472 first_path = gtk_tree_model_get_path(model, &fli.iter);
473 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
474 expand_tree(tree_view, &fli.iter, NULL, NULL);
476 /* ... and its parents */
477 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
478 path = gtk_tree_model_get_path(model, &parent);
479 gtk_tree_view_expand_row(tree_view, path, FALSE);
480 expand_tree(tree_view, &parent, NULL, NULL);
482 gtk_tree_path_free(path);
485 /* select our field's row */
486 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
489 /* And position the window so the selection is visible.
490 * Position the selection in the middle of the viewable
492 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
494 gtk_tree_path_free(first_path);
499 /* Calls functions for different mouse-button presses. */
501 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
503 GdkEventButton *event_button = NULL;
505 if(widget == NULL || event == NULL || data == NULL) {
509 if(event->type == GDK_BUTTON_PRESS) {
510 event_button = (GdkEventButton *) event;
512 /* To qoute the "Gdk Event Structures" doc:
513 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
514 switch(event_button->button) {
517 return byte_view_select(widget, event_button);
519 return popup_menu_handler(widget, event, data);
533 byte_nb = gtk_notebook_new();
534 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
536 /* this will only have an effect, if no tabs are shown */
537 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
539 /* set the tabs scrollable, if they don't fit into the pane */
540 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
542 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
543 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
545 /* Add a placeholder byte view so that there's at least something
546 displayed in the byte view notebook. */
547 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
553 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
555 const guint8 *byte_data;
558 byte_data = get_byte_view_data_and_length(bv, &byte_len);
559 if (byte_data == NULL) {
560 /* This must be the dummy byte view if no packet is selected. */
563 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
567 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
568 proto_tree *tree, GtkWidget *tree_view)
570 GtkWidget *byte_view, *byte_scrollw, *label;
574 /* Byte view. Create a scrolled window for the text. */
575 byte_scrollw = scrolled_window_new(NULL, NULL);
576 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
578 /* Add scrolled pane to tabbed window */
579 label = gtk_label_new(name);
580 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
582 gtk_widget_show(byte_scrollw);
584 byte_view = gtk_text_view_new();
585 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
586 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
587 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
588 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
589 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
590 gtk_text_buffer_create_tag(buf, "reverse",
591 "font-desc", user_font_get_regular(),
592 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
593 "background-gdk", &style->base[GTK_STATE_SELECTED],
595 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
596 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
597 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
599 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
600 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
601 g_object_get_data(G_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
603 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
604 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
606 gtk_widget_show(byte_view);
608 /* no tabs if this is the first page */
609 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
610 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
612 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
614 /* set this page (this will print the packet data) */
615 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb),
616 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
622 add_main_byte_views(epan_dissect_t *edt)
624 add_byte_views(edt, tree_view, byte_nb_ptr);
628 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
629 GtkWidget *byte_nb_ptr)
635 * Get rid of all the old notebook tabs.
637 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
638 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
641 * Add to the specified byte view notebook tabs for hex dumps
642 * of all the data sources for the specified frame.
644 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
646 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
651 * Initially select the first byte view.
653 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
658 static GtkWidget *savehex_dlg=NULL;
661 savehex_dlg_destroy_cb(void)
667 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
669 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
671 gboolean end_of_line = TRUE; /* Initial state is end of line */
672 int byte_line_part_length;
677 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
678 hex_str = g_string_new("");
679 char_str= g_string_new("");
684 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
687 g_string_append_printf(hex_str," %02x",*data_p);
689 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
694 /* Look ahead to see if this is the end of the data */
695 byte_line_part_length = (++i) % byte_line_length;
697 /* End of data - need to fill in spaces in hex string and then do "end of line".
700 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
701 g_string_append(hex_str," "); /* Three spaces for each missing byte */
705 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
711 g_string_append(copy_buffer, hex_str->str);
713 /* Two spaces between hex and text */
714 g_string_append_c(copy_buffer, ' ');
715 g_string_append_c(copy_buffer, ' ');
716 g_string_append(copy_buffer, char_str->str);
718 /* Setup ready for next line */
719 g_string_assign(char_str,"");
720 g_string_assign(hex_str, "\n");
724 g_string_free(hex_str, TRUE);
725 g_string_free(char_str, TRUE);
729 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
734 if(isprint(*data_p)) {
736 } else if(*data_p==0x0a) {
737 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
739 return 1; /* Just ignore non-printable bytes */
741 g_string_append_c(copy_buffer,to_append);
746 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
748 g_string_append_printf(copy_buffer, "%02x", *data_p);
753 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
758 int bytes_consumed = 0;
761 const guint8* data_p;
763 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
765 bv = get_notebook_bv_ptr(byte_nb_ptr);
767 /* shouldn't happen */
768 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
772 data_p = get_byte_view_data_and_length(bv, &len);
773 g_assert(data_p != NULL);
775 flags = data_type & CD_FLAGSMASK;
776 data_type = data_type & CD_TYPEMASK;
778 if(flags & CD_FLAGS_SELECTEDONLY) {
781 /* Get the start and end of the highlighted bytes.
782 * XXX The keys appear to be REVERSED start <-> end throughout this file!
783 * Should this be fixed? There is one exception - packet_hex_reprint,
784 * so can't just change it round without functional change.
786 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
787 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
789 if(start >= 0 && end > start && (end - start <= (int)len)) {
797 /* This is too different from other text formats - handle separately */
798 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
801 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
802 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
805 /* Completely different logic to text copies - leave copy buffer alone */
806 copy_binary_to_clipboard(data_p,len);
809 /* Incrementally write to text buffer in various formats */
813 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
816 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
819 g_assert_not_reached();
823 g_assert(bytes_consumed>0);
824 data_p += bytes_consumed;
825 len -= bytes_consumed;
830 if(copy_buffer->len > 0) {
831 copy_to_clipboard(copy_buffer);
834 g_string_free(copy_buffer, TRUE);
837 /* save the current highlighted hex data */
839 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
844 const guint8 *data_p = NULL;
845 const char *file = NULL;
847 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
849 if (!file ||! *file) {
850 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
854 /* Must check if file name exists first */
856 bv = get_notebook_bv_ptr(byte_nb_ptr);
858 /* shouldn't happen */
859 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
863 * Retrieve the info we need
865 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
866 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
867 data_p = get_byte_view_data_and_length(bv, &len);
869 if (data_p == NULL || start == -1 || start > end) {
870 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
871 "No data selected to save!");
875 fd = eth_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
877 open_failure_alert_box(file, errno, TRUE);
880 if (eth_write(fd, data_p + start, end - start) < 0) {
881 write_failure_alert_box(file, errno);
885 if (eth_close(fd) < 0) {
886 write_failure_alert_box(file, errno);
890 /* Get rid of the dialog box */
891 window_destroy(GTK_WIDGET(savehex_dlg));
894 /* Launch the dialog box to put up the file selection box etc */
895 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
899 const guint8 *data_p = NULL;
906 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
910 /* don't show up the dialog, if no data has to be saved */
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!");
917 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
918 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
919 data_p = get_byte_view_data_and_length(bv, &len);
921 if (data_p == NULL || start == -1 || start > end) {
922 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
926 /* if the window is already open, bring it to front */
928 reactivate_window(savehex_dlg);
933 * Build the dialog box we need.
935 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
938 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
939 end - start, plurality(end - start, "byte", "bytes"));
940 dlg_lb = gtk_label_new(label);
942 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
943 gtk_widget_show(dlg_lb);
945 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
947 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
948 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
950 window_destroy(savehex_dlg);
956 /* Update the progress bar this many times when reading a file. */
957 #define N_PROGBAR_UPDATES 100
961 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
962 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
963 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
964 * presumably why there's a progress bar for it.)
966 * Perhaps what's needed is a custom widget (either one that lets you stuff
967 * text into it more quickly, or one that's a "virtual" widget so that the
968 * text for a row is constructed, via a callback, when the row is to be
969 * displayed). A custom widget might also let us treat the offset, hex
970 * data, and ASCII data as three columns, so you can select purely in
971 * the hex dump column.
974 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
975 int bend, int astart, int aend, int encoding)
977 int i = 0, j, k, cur;
978 guchar line[MAX_LINES_LEN + 1];
979 static guchar hexchars[16] = {
980 '0', '1', '2', '3', '4', '5', '6', '7',
981 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
983 unsigned int use_digits;
984 gboolean reverse, newreverse;
985 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
986 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
988 const char *revstyle;
989 GtkTextMark *mark = NULL;
991 progdlg_t *progbar = NULL;
993 gboolean progbar_stop_flag;
994 GTimeVal progbar_start_time;
995 gchar progbar_status_str[100];
996 int progbar_nextstep;
999 gtk_text_buffer_set_text(buf, "", 0);
1000 gtk_text_buffer_get_start_iter(buf, &iter);
1002 gtk_text_view_set_buffer( bv_text_view, NULL);
1005 * How many of the leading digits of the offset will we supply?
1006 * We always supply at least 4 digits, but if the maximum offset
1007 * won't fit in 4 digits, we use as many digits as will be needed.
1009 if (((len - 1) & 0xF0000000) != 0)
1010 use_digits = 8; /* need all 8 digits */
1011 else if (((len - 1) & 0x0F000000) != 0)
1012 use_digits = 7; /* need 7 digits */
1013 else if (((len - 1) & 0x00F00000) != 0)
1014 use_digits = 6; /* need 6 digits */
1015 else if (((len - 1) & 0x000F0000) != 0)
1016 use_digits = 5; /* need 5 digits */
1018 use_digits = 4; /* we'll supply 4 digits */
1020 /* Record the number of digits in this text view. */
1021 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1023 /* Update the progress bar when it gets to this value. */
1024 progbar_nextstep = 0;
1025 /* When we reach the value that triggers a progress bar update,
1026 bump that value by this amount. */
1027 progbar_quantum = len/N_PROGBAR_UPDATES;
1028 /* Progress so far. */
1031 progbar_stop_flag = FALSE;
1032 g_get_current_time(&progbar_start_time);
1036 /* Create the progress bar if necessary.
1037 We check on every iteration of the loop, so that it takes no
1038 longer than the standard time to create it (otherwise, for a
1039 large packet, we might take considerably longer than that standard
1040 time in order to get to the next progress bar step). */
1041 if (progbar == NULL)
1042 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1045 &progbar_start_time,
1048 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1049 when we update it, we have to run the GTK+ main loop to get it
1050 to repaint what's pending, and doing so may involve an "ioctl()"
1051 to see if there's any pending input from an X server, and doing
1052 that for every packet can be costly, especially on a big file. */
1053 if (i >= progbar_nextstep) {
1054 /* let's not divide by zero. I should never be started
1055 * with count == 0, so let's assert that
1058 progbar_val = (gfloat) i / len;
1060 if (progbar != NULL) {
1061 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1062 "%4u of %u bytes", i, len);
1063 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1066 progbar_nextstep += progbar_quantum;
1069 if (progbar_stop_flag) {
1070 /* Well, the user decided to abort the operation. Just stop,
1071 and arrange to return TRUE to our caller, so they know it
1072 was stopped explicitly. */
1076 /* Print the line number */
1080 c = (i >> (j*4)) & 0xF;
1081 line[cur++] = hexchars[c];
1086 /* Display with inverse video ? */
1087 if (prefs.gui_hex_dump_highlight_style)
1088 revstyle = "reverse";
1092 /* Do we start in reverse? */
1093 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1095 k = i + BYTE_VIEW_WIDTH;
1097 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1101 /* Print the hex bit */
1104 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1105 line[cur++] = hexchars[pd[i] & 0x0f];
1107 line[cur++] = ' '; line[cur++] = ' ';
1110 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1111 /* Have we gone from reverse to plain? */
1112 if (reverse && (reverse != newreverse)) {
1113 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1117 /* Inter byte space if not at end of line */
1120 /* insert a space every BYTE_VIEW_SEP bytes */
1121 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1125 /* Have we gone from plain to reversed? */
1126 if (!reverse && (reverse != newreverse)) {
1127 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1129 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1132 reverse = newreverse;
1135 /* Print remaining part of line */
1136 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1141 /* Print some space at the end of the line */
1142 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1144 /* Print the ASCII bit */
1146 /* Do we start in reverse? */
1147 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1149 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1156 if (encoding == CHAR_ASCII) {
1159 else if (encoding == CHAR_EBCDIC) {
1160 c = EBCDIC_to_ASCII1(pd[i]);
1163 g_assert_not_reached();
1165 line[cur++] = isprint(c) ? c : '.';
1170 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1171 /* Have we gone from reverse to plain? */
1172 if (reverse && (reverse != newreverse)) {
1173 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1179 /* insert a space every BYTE_VIEW_SEP bytes */
1180 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1184 /* Have we gone from plain to reversed? */
1185 if (!reverse && (reverse != newreverse)) {
1186 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1190 reverse = newreverse;
1192 /* Print remaining part of line */
1194 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1199 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1200 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1206 /* We're done printing the packets; destroy the progress bar if
1208 if (progbar != NULL)
1209 destroy_progress_dlg(progbar);
1211 /* scroll text into position */
1213 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1216 gtk_text_view_set_buffer( bv_text_view, buf);
1219 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1220 gtk_text_buffer_delete_mark(buf, mark);
1222 g_object_unref(buf);
1226 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1227 field_info *finfo, guint len)
1229 /* do the initial printing and save the information needed */
1230 /* to redraw the display if preferences change. */
1232 int bstart = -1, bend = -1, blen = -1;
1233 int astart = -1, aend = -1, alen = -1;
1235 if (finfo != NULL) {
1236 bstart = finfo->start;
1237 blen = finfo->length;
1238 astart = finfo->appendix_start;
1239 alen = finfo->appendix_length;
1241 if (bstart >= 0 && blen >= 0) {
1242 bend = bstart + blen;
1244 if (astart >= 0 && alen >= 0) {
1245 aend = astart + alen;
1248 /* save the information needed to redraw the text */
1249 /* should we save the fd & finfo pointers instead ?? */
1250 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1251 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1252 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1253 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1254 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1255 GUINT_TO_POINTER((guint)fd->flags.encoding));
1257 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1261 * Redraw the text using the saved information; usually called if
1262 * the preferences have changed.
1265 packet_hex_reprint(GtkWidget *bv)
1267 int start, end, encoding;
1272 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1273 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1274 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1275 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1276 data = get_byte_view_data_and_length(bv, &len);
1277 g_assert(data != NULL);
1278 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1280 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1283 /* List of all protocol tree widgets, so we can globally set the selection
1284 mode and font of all of them. */
1285 static GList *ptree_widgets;
1287 /* Add a protocol tree widget to the list of protocol tree widgets. */
1288 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1291 remember_ptree_widget(GtkWidget *ptreew)
1293 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1295 /* Catch the "destroy" event on the widget, so that we remove it from
1296 the list when it's destroyed. */
1297 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1300 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1302 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1304 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1307 /* Set the selection mode of a given packet tree window. */
1309 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1311 GtkTreeSelection *selection;
1313 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1314 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1315 I think "browse" in Wireshark makes more sense than "SINGLE" in
1318 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1321 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1326 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1328 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1331 /* Set the selection mode of all packet tree windows. */
1333 set_ptree_sel_browse_all(gboolean val)
1335 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1339 set_ptree_font_cb(gpointer data, gpointer user_data)
1341 gtk_widget_modify_font((GtkWidget *)data,
1342 (PangoFontDescription *)user_data);
1346 set_ptree_font_all(PangoFontDescription *font)
1348 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1352 gboolean colors_ok = FALSE;
1353 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1354 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1355 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1356 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1357 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1358 GdkColor hidden_proto_item = { 0, 0x4400, 0x4400, 0x4400 }; /* gray */
1360 void proto_draw_colors_init(void)
1366 get_color(&expert_color_chat);
1367 get_color(&expert_color_note);
1368 get_color(&expert_color_warn);
1369 get_color(&expert_color_error);
1370 get_color(&expert_color_foreground);
1371 get_color(&hidden_proto_item);
1377 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1378 GtkCellRenderer *cell,
1379 GtkTreeModel *tree_model,
1385 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1388 proto_draw_colors_init();
1391 /* for the various possible attributes, see:
1392 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1394 * color definitions can be found at:
1395 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1396 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1399 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1400 * weight/style: doesn't take any effect
1403 /* for each field, we have to reset the renderer attributes */
1404 g_object_set (cell, "foreground-set", FALSE, NULL);
1406 g_object_set (cell, "background-set", FALSE, NULL);
1408 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1409 g_object_set (cell, "underline-set", FALSE, NULL);
1411 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1412 g_object_set (cell, "style-set", FALSE, NULL);*/
1414 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1415 g_object_set (cell, "weight-set", FALSE, NULL);*/
1417 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1418 /* we use "[...]" to mark generated items, no need to change things here */
1420 /* as some fonts don't support italic, don't use this */
1421 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1422 g_object_set (cell, "style-set", TRUE, NULL);
1424 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1425 g_object_set (cell, "weight-set", TRUE, NULL);*/
1426 } else if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1427 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1428 g_object_set (cell, "foreground-set", TRUE, NULL);
1431 if(fi->hfinfo->type == FT_PROTOCOL) {
1432 g_object_set (cell, "background", "gray90", NULL);
1433 g_object_set (cell, "background-set", TRUE, NULL);
1434 g_object_set (cell, "foreground", "black", NULL);
1435 g_object_set (cell, "foreground-set", TRUE, NULL);
1436 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1437 g_object_set (cell, "weight-set", TRUE, NULL);*/
1440 if((fi->hfinfo->type == FT_FRAMENUM) ||
1441 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1442 render_as_url(cell);
1445 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1446 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1448 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1449 g_object_set (cell, "background-set", TRUE, NULL);
1452 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1453 g_object_set (cell, "background-set", TRUE, NULL);
1456 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1457 g_object_set (cell, "background-set", TRUE, NULL);
1460 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1461 g_object_set (cell, "background-set", TRUE, NULL);
1464 g_assert_not_reached();
1466 g_object_set (cell, "foreground", "black", NULL);
1467 g_object_set (cell, "foreground-set", TRUE, NULL);
1472 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1474 GtkWidget *tv_scrollw, *tree_view;
1475 GtkTreeStore *store;
1476 GtkCellRenderer *renderer;
1477 GtkTreeViewColumn *column;
1481 tv_scrollw = scrolled_window_new(NULL, NULL);
1482 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1485 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1486 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1487 g_object_unref(G_OBJECT(store));
1488 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1489 renderer = gtk_cell_renderer_text_new();
1490 g_object_set (renderer, "ypad", 0, NULL);
1491 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1492 -1, "Name", renderer,
1494 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1496 gtk_tree_view_column_set_cell_data_func(column,
1502 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1503 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1504 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1505 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1506 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1507 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1508 gtk_widget_modify_font(tree_view, user_font_get_regular());
1509 remember_ptree_widget(tree_view);
1511 *tree_view_p = tree_view;
1516 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1519 for(i=0; i < num_tree_types; i++) {
1520 tree_is_expanded[i] = TRUE;
1522 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1525 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1528 for(i=0; i < num_tree_types; i++) {
1529 tree_is_expanded[i] = FALSE;
1531 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1535 struct proto_tree_draw_info {
1536 GtkTreeView *tree_view;
1541 main_proto_tree_draw(proto_tree *protocol_tree)
1543 proto_tree_draw(protocol_tree, tree_view);
1548 tree_view_follow_link(field_info *fi)
1552 if(fi->hfinfo->type == FT_FRAMENUM) {
1553 cf_goto_frame(&cfile, fi->value.value.uinteger);
1555 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1556 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1557 browser_open_url(url);
1563 /* If the user selected a position in the tree view, try to find
1564 * the item in the GUI proto_tree that corresponds to that byte, and
1567 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1569 GtkTreeSelection *sel;
1572 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1573 (gint) (((GdkEventButton *)event)->x),
1574 (gint) (((GdkEventButton *)event)->y),
1575 &path, NULL, NULL, NULL))
1577 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1579 /* if that's a doubleclick, try to follow the link */
1580 if(event->type == GDK_2BUTTON_PRESS) {
1581 GtkTreeModel *model;
1585 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1586 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1587 tree_view_follow_link(fi);
1590 else if (((GdkEventButton *)event)->button != 1) {
1591 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1592 gtk_tree_selection_select_path(sel, path);
1600 /* fill the whole protocol tree with the string values */
1602 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1604 GtkTreeStore *store;
1605 struct proto_tree_draw_info info;
1607 info.tree_view = GTK_TREE_VIEW(tree_view);
1610 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1613 * Clear out any crud left over in the display of the protocol
1614 * tree, by removing all nodes from the tree.
1615 * This is how it's done in testgtk.c in GTK+.
1617 gtk_tree_store_clear(store);
1619 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1623 /* fill a single protocol tree item with the string value */
1625 proto_tree_draw_node(proto_node *node, gpointer data)
1627 struct proto_tree_draw_info info;
1628 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1630 field_info *fi = PITEM_FINFO(node);
1631 gchar label_str[ITEM_LABEL_LENGTH];
1633 gboolean is_leaf, is_expanded;
1634 GtkTreeStore *store;
1638 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
1641 /* was a free format label produced? */
1643 label_ptr = fi->rep->representation;
1645 else { /* no, make a generic label */
1646 label_ptr = label_str;
1647 proto_item_fill_label(fi, label_str);
1650 if (node->first_child != NULL) {
1652 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1653 if (tree_is_expanded[fi->tree_type]) {
1657 is_expanded = FALSE;
1662 is_expanded = FALSE;
1665 if (PROTO_ITEM_IS_GENERATED(node)) {
1666 label_ptr = g_strdup_printf("[%s]", label_ptr);
1667 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
1668 label_ptr = g_strdup_printf("<%s>", label_ptr);
1671 info.tree_view = parent_info->tree_view;
1672 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1673 gtk_tree_store_append(store, &iter, parent_info->iter);
1674 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1676 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
1682 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1683 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1685 gtk_tree_view_expand_to_path(info.tree_view, path);
1687 gtk_tree_view_collapse_row(info.tree_view, path);
1688 gtk_tree_path_free(path);
1693 * Clear the hex dump and protocol tree panes.
1696 clear_tree_and_hex_views(void)
1698 /* Clear the hex dump by getting rid of all the byte views. */
1699 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1700 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1702 /* Add a placeholder byte view so that there's at least something
1703 displayed in the byte view notebook. */
1704 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1706 /* Clear the protocol tree by removing all nodes in the ctree.
1707 This is how it's done in testgtk.c in GTK+ */
1708 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));