2 * Routines for GTK+ packet display
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * Jeff Foster, 2001/03/12, added support for displaying named
11 * data sources as tabbed hex windows
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
43 #include <gdk/gdkkeysyms.h>
47 #include <epan/epan_dissect.h>
49 #include <epan/packet.h>
50 #include <epan/charsets.h>
51 #include <epan/prefs.h>
52 #include <epan/filesystem.h>
54 #include "../isprint.h"
55 #include "../alert_box.h"
56 #include "../simple_dialog.h"
57 #include "../progress_dlg.h"
58 #include "../ui_util.h"
59 #include <wsutil/file_util.h>
62 #include "gtk/color_utils.h"
63 #include "gtk/capture_file_dlg.h"
64 #include "gtk/packet_win.h"
65 #include "gtk/file_dlg.h"
66 #include "gtk/gui_utils.h"
67 #include "gtk/gtkglobals.h"
68 #include "gtk/font_utils.h"
69 #include "gtk/webbrowser.h"
71 #include "gtk/menus.h"
72 #include "gtk/main_proto_draw.h"
75 #include <gdk/gdkwin32.h>
77 #include "file_dlg_win32.h"
81 #define BYTE_VIEW_WIDTH 16
82 #define BYTE_VIEW_SEP 8
84 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
85 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
86 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
87 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
88 #define E_BYTE_VIEW_START_KEY "byte_view_start"
89 #define E_BYTE_VIEW_END_KEY "byte_view_end"
90 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
91 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
92 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
95 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
96 proto_tree *tree, GtkWidget *tree_view);
99 proto_tree_draw_node(proto_node *node, gpointer data);
101 /* Get the current text window for the notebook. */
103 get_notebook_bv_ptr(GtkWidget *nb_ptr)
108 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
109 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
111 return GTK_BIN(bv_page)->child;
117 * Get the data and length for a byte view, given the byte view page.
118 * Return the pointer, or NULL on error, and set "*data_len" to the length.
121 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
123 tvbuff_t *byte_view_tvb;
124 const guint8 *data_ptr;
126 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
127 if (byte_view_tvb == NULL)
130 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
131 *data_len = tvb_length(byte_view_tvb);
136 * Set the current text window for the notebook to the window that
137 * refers to a particular tvbuff.
140 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
143 GtkWidget *bv_page, *bv;
147 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
149 bv = GTK_BIN(bv_page)->child;
150 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
153 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
159 /* Redraw a given byte view window. */
161 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
167 bv = get_notebook_bv_ptr(nb);
169 data = get_byte_view_data_and_length(bv, &len);
171 packet_hex_print(bv, data, fd, finfo, len);
175 /* Redraw all byte view windows. */
177 redraw_hex_dump_all(void)
179 if (cfile.current_frame != NULL)
180 redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
182 redraw_hex_dump_packet_wins();
184 /* XXX - this is a hack, to workaround a bug in GTK2.x!
185 when changing the font size, even refilling of the corresponding
186 gtk_text_buffer doesn't seem to trigger an update.
187 The only workaround is to freshly select the frame, which will remove any
188 existing notebook tabs and "restart" the whole byte view again. */
189 if (cfile.current_frame != NULL) {
190 cfile.current_row = -1;
191 cf_goto_frame(&cfile, cfile.current_frame->num);
196 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
197 GtkTreePath *path _U_, gpointer user_data _U_)
202 model = gtk_tree_view_get_model(tree_view);
203 gtk_tree_model_get(model, iter, 1, &finfo, -1);
207 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
208 * are thus presumably leaf nodes and cannot be expanded.
210 if (finfo->tree_type != -1) {
211 g_assert(finfo->tree_type >= 0 &&
212 finfo->tree_type < num_tree_types);
213 tree_is_expanded[finfo->tree_type] = TRUE;
218 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
219 GtkTreePath *path _U_, gpointer user_data _U_)
224 model = gtk_tree_view_get_model(tree_view);
225 gtk_tree_model_get(model, iter, 1, &finfo, -1);
229 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
230 * are thus presumably leaf nodes and cannot be collapsed.
232 if (finfo->tree_type != -1) {
233 g_assert(finfo->tree_type >= 0 &&
234 finfo->tree_type < num_tree_types);
235 tree_is_expanded[finfo->tree_type] = FALSE;
239 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
240 #define BYTES_PER_LINE 16 /* max byte values in a line */
241 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
242 /* max number of characters hex dump takes -
243 2 digits plus trailing blank
244 plus separator between first and
246 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
247 /* number of characters those bytes take;
248 3 characters per byte of hex dump,
249 2 blanks separating hex from ASCII,
250 1 character per byte of ASCII dump */
251 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
252 /* number of characters per line;
253 offset, 2 blanks separating offset
254 from data dump, data dump */
255 #define MAX_LINES 100
256 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
258 /* Which byte the offset is referring to. Associates
259 * whitespace with the preceding digits. */
261 byte_num(int offset, int start_point)
263 return (offset - start_point) / 3;
266 struct field_lookup_info {
272 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
276 struct field_lookup_info *fli = (struct field_lookup_info *)data;
278 gtk_tree_model_get(model, iter, 1, &fi, -1);
286 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
288 struct field_lookup_info fli;
290 g_assert(finfo != NULL);
292 model = gtk_tree_view_get_model(tree_view);
294 gtk_tree_model_foreach(model, lookup_finfo, &fli);
296 return gtk_tree_model_get_path(model, &fli.iter);
299 /* If the user selected a certain byte in the byte view, try to find
300 * the item in the GUI proto_tree that corresponds to that byte, and:
302 * if we succeed, select it, and return TRUE;
303 * if we fail, return FALSE. */
305 byte_view_select(GtkWidget *widget, GdkEventButton *event)
308 GtkTreeView *tree_view;
309 GtkTextView *bv = GTK_TEXT_VIEW(widget);
326 * Get the number of digits of offset being displayed, and
327 * compute the columns of various parts of the display.
329 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
332 * The column of the first hex digit in the first half.
333 * That starts after "ndigits" digits of offset and two
336 digits_start_1 = ndigits + 2;
339 * The column of the last hex digit in the first half.
340 * There are BYTES_PER_LINE/2 bytes displayed in the first
341 * half; there are 2 characters per byte, plus a separating
342 * blank after all but the last byte's characters.
344 * Then subtract 1 to get the last column of the first half
345 * rather than the first column after the first half.
347 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
348 (BYTES_PER_LINE/2 - 1) - 1;
351 * The column of the first hex digit in the second half.
352 * Add back the 1 to get the first column after the first
353 * half, and then add 2 for the 2 separating blanks between
356 digits_start_2 = digits_end_1 + 3;
359 * The column of the last hex digit in the second half.
360 * Add the same value we used to get "digits_end_1" from
363 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
364 (BYTES_PER_LINE/2 - 1) - 1;
367 * The column of the first "text dump" character in the first half.
368 * Add back the 1 to get the first column after the second
369 * half's hex dump, and then add 3 for the 3 separating blanks
370 * between the hex and text dummp.
372 text_start_1 = digits_end_2 + 4;
375 * The column of the last "text dump" character in the first half.
376 * There are BYTES_PER_LINE/2 bytes displayed in the first
377 * half; there is 1 character per byte.
379 * Then subtract 1 to get the last column of the first half
380 * rather than the first column after the first half.
382 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
385 * The column of the first "text dump" character in the second half.
386 * Add back the 1 to get the first column after the first half,
387 * and then add 1 for the separating blank between the halves.
389 text_start_2 = text_end_1 + 2;
392 * The column of the last "text dump" character in second half.
393 * Add the same value we used to get "text_end_1" from
396 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
398 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
401 * Somebody clicked on the dummy byte view; do nothing.
405 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
406 E_BYTE_VIEW_TREE_VIEW_PTR));
408 /* get the row/column selected */
409 gtk_text_view_window_to_buffer_coords(bv,
410 gtk_text_view_get_window_type(bv, event->window),
411 (gint) event->x, (gint) event->y, &x, &y);
412 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
413 row = gtk_text_iter_get_line(&iter);
414 column = gtk_text_iter_get_line_offset(&iter);
416 /* Given the column and row, determine which byte offset
417 * the user clicked on. */
418 if (column >= digits_start_1 && column <= digits_end_1) {
419 byte = byte_num(column, digits_start_1);
424 else if (column >= digits_start_2 && column <= digits_end_2) {
425 byte = byte_num(column, digits_start_2);
431 else if (column >= text_start_1 && column <= text_end_1) {
432 byte = column - text_start_1;
434 else if (column >= text_start_2 && column <= text_end_2) {
435 byte = 8 + column - text_start_2;
438 /* The user didn't select a hex digit or
439 * text-dump character. */
443 /* Add the number of bytes from the previous rows. */
446 /* Get the data source tvbuff */
447 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
449 return highlight_field(tvb, byte, tree_view, tree);
452 /* This highlights the field in the proto tree that is at position byte */
454 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
458 GtkTreePath *first_path, *path;
460 struct field_lookup_info fli;
463 /* Find the finfo that corresponds to our byte. */
464 finfo = proto_find_field_from_offset(tree, byte, tvb);
470 model = gtk_tree_view_get_model(tree_view);
472 gtk_tree_model_foreach(model, lookup_finfo, &fli);
474 /* Expand our field's row */
475 first_path = gtk_tree_model_get_path(model, &fli.iter);
476 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
477 expand_tree(tree_view, &fli.iter, NULL, NULL);
479 /* ... and its parents */
480 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
481 path = gtk_tree_model_get_path(model, &parent);
482 gtk_tree_view_expand_row(tree_view, path, FALSE);
483 expand_tree(tree_view, &parent, NULL, NULL);
485 gtk_tree_path_free(path);
488 /* select our field's row */
489 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
492 /* And position the window so the selection is visible.
493 * Position the selection in the middle of the viewable
495 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
497 gtk_tree_path_free(first_path);
502 /* Calls functions for different mouse-button presses. */
504 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
506 GdkEventButton *event_button = NULL;
508 if(widget == NULL || event == NULL || data == NULL) {
512 if(event->type == GDK_BUTTON_PRESS) {
513 event_button = (GdkEventButton *) event;
515 /* To qoute the "Gdk Event Structures" doc:
516 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
517 switch(event_button->button) {
520 return byte_view_select(widget, event_button);
522 return popup_menu_handler(widget, event, data);
536 byte_nb = gtk_notebook_new();
537 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
539 /* this will only have an effect, if no tabs are shown */
540 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
542 /* set the tabs scrollable, if they don't fit into the pane */
543 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
545 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
546 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
548 /* Add a placeholder byte view so that there's at least something
549 displayed in the byte view notebook. */
550 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
556 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
558 const guint8 *byte_data;
561 byte_data = get_byte_view_data_and_length(bv, &byte_len);
562 if (byte_data == NULL) {
563 /* This must be the dummy byte view if no packet is selected. */
566 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
570 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
571 proto_tree *tree, GtkWidget *tree_view)
573 GtkWidget *byte_view, *byte_scrollw, *label;
577 /* Byte view. Create a scrolled window for the text. */
578 byte_scrollw = scrolled_window_new(NULL, NULL);
579 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
581 /* Add scrolled pane to tabbed window */
582 label = gtk_label_new(name);
583 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
585 gtk_widget_show(byte_scrollw);
587 byte_view = gtk_text_view_new();
588 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
589 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
590 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
591 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
592 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
593 gtk_text_buffer_create_tag(buf, "reverse",
594 "font-desc", user_font_get_regular(),
595 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
596 "background-gdk", &style->base[GTK_STATE_SELECTED],
598 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
599 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
600 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
602 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
603 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
604 g_object_get_data(G_OBJECT(popup_menu_object), PM_HEXDUMP_KEY));
606 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
607 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
609 gtk_widget_show(byte_view);
611 /* no tabs if this is the first page */
612 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
613 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
615 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
617 /* set this page (this will print the packet data) */
618 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
619 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
625 add_main_byte_views(epan_dissect_t *edt)
627 add_byte_views(edt, tree_view, byte_nb_ptr);
631 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
632 GtkWidget *byte_nb_ptr)
638 * Get rid of all the old notebook tabs.
640 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
641 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
644 * Add to the specified byte view notebook tabs for hex dumps
645 * of all the data sources for the specified frame.
647 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
649 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
654 * Initially select the first byte view.
656 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
661 static GtkWidget *savehex_dlg=NULL;
664 savehex_dlg_destroy_cb(void)
670 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
672 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
674 gboolean end_of_line = TRUE; /* Initial state is end of line */
675 int byte_line_part_length;
680 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
681 hex_str = g_string_new("");
682 char_str= g_string_new("");
687 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
690 g_string_append_printf(hex_str," %02x",*data_p);
692 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
697 /* Look ahead to see if this is the end of the data */
698 byte_line_part_length = (++i) % byte_line_length;
700 /* End of data - need to fill in spaces in hex string and then do "end of line".
703 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
704 g_string_append(hex_str," "); /* Three spaces for each missing byte */
708 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
714 g_string_append(copy_buffer, hex_str->str);
716 /* Two spaces between hex and text */
717 g_string_append_c(copy_buffer, ' ');
718 g_string_append_c(copy_buffer, ' ');
719 g_string_append(copy_buffer, char_str->str);
721 /* Setup ready for next line */
722 g_string_assign(char_str,"");
723 g_string_assign(hex_str, "\n");
727 g_string_free(hex_str, TRUE);
728 g_string_free(char_str, TRUE);
732 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
737 if(isprint(*data_p)) {
739 } else if(*data_p==0x0a) {
740 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
742 return 1; /* Just ignore non-printable bytes */
744 g_string_append_c(copy_buffer,to_append);
749 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
751 g_string_append_printf(copy_buffer, "%02x", *data_p);
756 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
761 int bytes_consumed = 0;
764 const guint8* data_p;
766 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
768 bv = get_notebook_bv_ptr(byte_nb_ptr);
770 /* shouldn't happen */
771 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
775 data_p = get_byte_view_data_and_length(bv, &len);
776 g_assert(data_p != NULL);
778 flags = data_type & CD_FLAGSMASK;
779 data_type = data_type & CD_TYPEMASK;
781 if(flags & CD_FLAGS_SELECTEDONLY) {
784 /* Get the start and end of the highlighted bytes.
785 * XXX The keys appear to be REVERSED start <-> end throughout this file!
786 * Should this be fixed? There is one exception - packet_hex_reprint,
787 * so can't just change it round without functional change.
789 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
790 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
792 if(start >= 0 && end > start && (end - start <= (int)len)) {
800 /* This is too different from other text formats - handle separately */
801 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
804 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
805 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
808 /* Completely different logic to text copies - leave copy buffer alone */
809 copy_binary_to_clipboard(data_p,len);
812 /* Incrementally write to text buffer in various formats */
816 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
819 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
822 g_assert_not_reached();
826 g_assert(bytes_consumed>0);
827 data_p += bytes_consumed;
828 len -= bytes_consumed;
833 if(copy_buffer->len > 0) {
834 copy_to_clipboard(copy_buffer);
837 g_string_free(copy_buffer, TRUE);
840 /* save the current highlighted hex data */
842 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
847 const guint8 *data_p = NULL;
850 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
852 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
853 if (!file ||! *file) {
854 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
859 if (test_for_directory(file) == EISDIR) {
860 /* It's a directory - set the file selection box to display that
861 directory, and leave the selection box displayed. */
862 set_last_open_dir(file);
864 file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
865 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
866 return FALSE; /* do gtk_dialog_run again */
869 /* XXX: Must check if file name exists first */
871 bv = get_notebook_bv_ptr(byte_nb_ptr);
873 /* shouldn't happen */
874 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
879 * Retrieve the info we need
881 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
882 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
883 data_p = get_byte_view_data_and_length(bv, &len);
885 if (data_p == NULL || start == -1 || start > end) {
886 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
887 "No data selected to save!");
892 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
894 open_failure_alert_box(file, errno, TRUE);
898 if (ws_write(fd, data_p + start, end - start) < 0) {
899 write_failure_alert_box(file, errno);
904 if (ws_close(fd) < 0) {
905 write_failure_alert_box(file, errno);
910 /* Get rid of the dialog box */
912 #if 0 /* being handled by caller (for now) */
913 window_destroy(GTK_WIDGET(savehex_dlg));
918 /* Launch the dialog box to put up the file selection box etc */
919 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
923 const guint8 *data_p = NULL;
930 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
934 /* don't show up the dialog, if no data has to be saved */
935 bv = get_notebook_bv_ptr(byte_nb_ptr);
937 /* shouldn't happen */
938 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
941 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
942 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
943 data_p = get_byte_view_data_and_length(bv, &len);
945 if (data_p == NULL || start == -1 || start > end) {
946 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
950 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
951 /* if the window is already open, bring it to front */
953 reactivate_window(savehex_dlg);
958 * Build the dialog box we need.
960 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
961 #if GTK_CHECK_VERSION(2,8,0)
962 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
966 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
967 end - start, plurality(end - start, "byte", "bytes"));
968 dlg_lb = gtk_label_new(label);
970 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
971 gtk_widget_show(dlg_lb);
973 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
976 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
977 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
979 window_destroy(savehex_dlg);
982 /* "Run" the GtkFileChooserDialog. */
983 /* Upon exit: If "Accept" run the OK callback. */
984 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
985 /* If not accept (ie: cancel) destroy the window. */
986 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
987 /* return with a TRUE status so that the dialog window will be destroyed. */
988 /* Trying to re-run the dialog after popping up an alert box will not work */
989 /* since the user will not be able to dismiss the alert box. */
990 /* The (somewhat unfriendly) effect: the user must re-invoke the */
991 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
993 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
994 /* GtkFileChooserDialog. */
995 while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
996 if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
997 break; /* we're done */
1000 window_destroy(savehex_dlg);
1005 /* Update the progress bar this many times when reading a file. */
1006 #define N_PROGBAR_UPDATES 100
1010 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1011 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1012 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1013 * presumably why there's a progress bar for it.)
1015 * Perhaps what's needed is a custom widget (either one that lets you stuff
1016 * text into it more quickly, or one that's a "virtual" widget so that the
1017 * text for a row is constructed, via a callback, when the row is to be
1018 * displayed). A custom widget might also let us treat the offset, hex
1019 * data, and ASCII data as three columns, so you can select purely in
1020 * the hex dump column.
1023 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1024 int bend, int astart, int aend, int encoding)
1026 int i = 0, j, k, cur;
1027 guchar line[MAX_LINES_LEN + 1];
1028 static guchar hexchars[16] = {
1029 '0', '1', '2', '3', '4', '5', '6', '7',
1030 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1032 unsigned int use_digits;
1033 gboolean reverse, newreverse;
1034 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1035 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1037 const char *revstyle;
1038 GtkTextMark *mark = NULL;
1040 progdlg_t *progbar = NULL;
1042 gboolean progbar_stop_flag;
1043 GTimeVal progbar_start_time;
1044 gchar progbar_status_str[100];
1045 int progbar_nextstep;
1046 int progbar_quantum;
1048 gtk_text_buffer_set_text(buf, "", 0);
1049 gtk_text_buffer_get_start_iter(buf, &iter);
1051 gtk_text_view_set_buffer( bv_text_view, NULL);
1054 * How many of the leading digits of the offset will we supply?
1055 * We always supply at least 4 digits, but if the maximum offset
1056 * won't fit in 4 digits, we use as many digits as will be needed.
1058 if (((len - 1) & 0xF0000000) != 0)
1059 use_digits = 8; /* need all 8 digits */
1060 else if (((len - 1) & 0x0F000000) != 0)
1061 use_digits = 7; /* need 7 digits */
1062 else if (((len - 1) & 0x00F00000) != 0)
1063 use_digits = 6; /* need 6 digits */
1064 else if (((len - 1) & 0x000F0000) != 0)
1065 use_digits = 5; /* need 5 digits */
1067 use_digits = 4; /* we'll supply 4 digits */
1069 /* Record the number of digits in this text view. */
1070 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1072 /* Update the progress bar when it gets to this value. */
1073 progbar_nextstep = 0;
1074 /* When we reach the value that triggers a progress bar update,
1075 bump that value by this amount. */
1076 progbar_quantum = len/N_PROGBAR_UPDATES;
1077 /* Progress so far. */
1080 progbar_stop_flag = FALSE;
1081 g_get_current_time(&progbar_start_time);
1085 /* Create the progress bar if necessary.
1086 We check on every iteration of the loop, so that it takes no
1087 longer than the standard time to create it (otherwise, for a
1088 large packet, we might take considerably longer than that standard
1089 time in order to get to the next progress bar step). */
1090 if (progbar == NULL)
1091 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1094 &progbar_start_time,
1097 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1098 when we update it, we have to run the GTK+ main loop to get it
1099 to repaint what's pending, and doing so may involve an "ioctl()"
1100 to see if there's any pending input from an X server, and doing
1101 that for every packet can be costly, especially on a big file. */
1102 if (i >= progbar_nextstep) {
1103 /* let's not divide by zero. I should never be started
1104 * with count == 0, so let's assert that
1107 progbar_val = (gfloat) i / len;
1109 if (progbar != NULL) {
1110 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1111 "%4u of %u bytes", i, len);
1112 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1115 progbar_nextstep += progbar_quantum;
1118 if (progbar_stop_flag) {
1119 /* Well, the user decided to abort the operation. Just stop,
1120 and arrange to return TRUE to our caller, so they know it
1121 was stopped explicitly. */
1125 /* Print the line number */
1129 c = (i >> (j*4)) & 0xF;
1130 line[cur++] = hexchars[c];
1135 /* Display with inverse video ? */
1136 if (prefs.gui_hex_dump_highlight_style)
1137 revstyle = "reverse";
1141 /* Do we start in reverse? */
1142 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1144 k = i + BYTE_VIEW_WIDTH;
1146 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1150 /* Print the hex bit */
1153 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1154 line[cur++] = hexchars[pd[i] & 0x0f];
1156 line[cur++] = ' '; line[cur++] = ' ';
1159 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1160 /* Have we gone from reverse to plain? */
1161 if (reverse && (reverse != newreverse)) {
1162 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1166 /* Inter byte space if not at end of line */
1169 /* insert a space every BYTE_VIEW_SEP bytes */
1170 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1174 /* Have we gone from plain to reversed? */
1175 if (!reverse && (reverse != newreverse)) {
1176 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1178 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1181 reverse = newreverse;
1184 /* Print remaining part of line */
1185 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1190 /* Print some space at the end of the line */
1191 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1193 /* Print the ASCII bit */
1195 /* Do we start in reverse? */
1196 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1198 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1205 if (encoding == CHAR_ASCII) {
1208 else if (encoding == CHAR_EBCDIC) {
1209 c = EBCDIC_to_ASCII1(pd[i]);
1212 g_assert_not_reached();
1214 line[cur++] = isprint(c) ? c : '.';
1219 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1220 /* Have we gone from reverse to plain? */
1221 if (reverse && (reverse != newreverse)) {
1222 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1228 /* insert a space every BYTE_VIEW_SEP bytes */
1229 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1233 /* Have we gone from plain to reversed? */
1234 if (!reverse && (reverse != newreverse)) {
1235 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1239 reverse = newreverse;
1241 /* Print remaining part of line */
1243 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1248 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1249 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1255 /* We're done printing the packets; destroy the progress bar if
1257 if (progbar != NULL)
1258 destroy_progress_dlg(progbar);
1260 /* scroll text into position */
1262 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1265 gtk_text_view_set_buffer( bv_text_view, buf);
1268 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1269 gtk_text_buffer_delete_mark(buf, mark);
1271 g_object_unref(buf);
1275 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1276 field_info *finfo, guint len)
1278 /* do the initial printing and save the information needed */
1279 /* to redraw the display if preferences change. */
1281 int bstart = -1, bend = -1, blen = -1;
1282 int astart = -1, aend = -1, alen = -1;
1284 if (finfo != NULL) {
1285 bstart = finfo->start;
1286 blen = finfo->length;
1287 astart = finfo->appendix_start;
1288 alen = finfo->appendix_length;
1290 if (bstart >= 0 && blen >= 0) {
1291 bend = bstart + blen;
1293 if (astart >= 0 && alen >= 0) {
1294 aend = astart + alen;
1297 /* save the information needed to redraw the text */
1298 /* should we save the fd & finfo pointers instead ?? */
1299 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1300 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1301 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1302 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1303 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1304 GUINT_TO_POINTER((guint)fd->flags.encoding));
1306 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1310 * Redraw the text using the saved information; usually called if
1311 * the preferences have changed.
1314 packet_hex_reprint(GtkWidget *bv)
1316 int start, end, encoding;
1321 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1322 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1323 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1324 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1325 data = get_byte_view_data_and_length(bv, &len);
1326 g_assert(data != NULL);
1327 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1329 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1332 /* List of all protocol tree widgets, so we can globally set the selection
1333 mode and font of all of them. */
1334 static GList *ptree_widgets;
1336 /* Add a protocol tree widget to the list of protocol tree widgets. */
1337 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1340 remember_ptree_widget(GtkWidget *ptreew)
1342 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1344 /* Catch the "destroy" event on the widget, so that we remove it from
1345 the list when it's destroyed. */
1346 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1349 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1351 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1353 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1356 /* Set the selection mode of a given packet tree window. */
1358 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1360 GtkTreeSelection *selection;
1362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1363 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1364 I think "browse" in Wireshark makes more sense than "SINGLE" in
1367 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1370 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1375 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1377 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1380 /* Set the selection mode of all packet tree windows. */
1382 set_ptree_sel_browse_all(gboolean val)
1384 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1388 set_ptree_font_cb(gpointer data, gpointer user_data)
1390 gtk_widget_modify_font((GtkWidget *)data,
1391 (PangoFontDescription *)user_data);
1395 set_ptree_font_all(PangoFontDescription *font)
1397 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1401 gboolean colors_ok = FALSE;
1402 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1403 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1404 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1405 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1406 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1407 GdkColor hidden_proto_item = { 0, 0x4400, 0x4400, 0x4400 }; /* gray */
1409 void proto_draw_colors_init(void)
1415 get_color(&expert_color_chat);
1416 get_color(&expert_color_note);
1417 get_color(&expert_color_warn);
1418 get_color(&expert_color_error);
1419 get_color(&expert_color_foreground);
1420 get_color(&hidden_proto_item);
1426 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1427 GtkCellRenderer *cell,
1428 GtkTreeModel *tree_model,
1434 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1437 proto_draw_colors_init();
1440 /* for the various possible attributes, see:
1441 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1443 * color definitions can be found at:
1444 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1445 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1448 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1449 * weight/style: doesn't take any effect
1452 /* for each field, we have to reset the renderer attributes */
1453 g_object_set (cell, "foreground-set", FALSE, NULL);
1455 g_object_set (cell, "background-set", FALSE, NULL);
1457 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1458 g_object_set (cell, "underline-set", FALSE, NULL);
1460 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1461 g_object_set (cell, "style-set", FALSE, NULL);*/
1463 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1464 g_object_set (cell, "weight-set", FALSE, NULL);*/
1466 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1467 /* we use "[...]" to mark generated items, no need to change things here */
1469 /* as some fonts don't support italic, don't use this */
1470 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1471 g_object_set (cell, "style-set", TRUE, NULL);
1473 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1474 g_object_set (cell, "weight-set", TRUE, NULL);*/
1477 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1478 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1479 g_object_set (cell, "foreground-set", TRUE, NULL);
1482 if(fi->hfinfo->type == FT_PROTOCOL) {
1483 g_object_set (cell, "background", "gray90", NULL);
1484 g_object_set (cell, "background-set", TRUE, NULL);
1485 g_object_set (cell, "foreground", "black", NULL);
1486 g_object_set (cell, "foreground-set", TRUE, NULL);
1487 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1488 g_object_set (cell, "weight-set", TRUE, NULL);*/
1491 if((fi->hfinfo->type == FT_FRAMENUM) ||
1492 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1493 render_as_url(cell);
1496 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1497 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1499 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1500 g_object_set (cell, "background-set", TRUE, NULL);
1503 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1504 g_object_set (cell, "background-set", TRUE, NULL);
1507 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1508 g_object_set (cell, "background-set", TRUE, NULL);
1511 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1512 g_object_set (cell, "background-set", TRUE, NULL);
1515 g_assert_not_reached();
1517 g_object_set (cell, "foreground", "black", NULL);
1518 g_object_set (cell, "foreground-set", TRUE, NULL);
1523 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1525 GtkWidget *tv_scrollw, *tree_view;
1526 GtkTreeStore *store;
1527 GtkCellRenderer *renderer;
1528 GtkTreeViewColumn *column;
1532 tv_scrollw = scrolled_window_new(NULL, NULL);
1533 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1536 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1537 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1538 g_object_unref(G_OBJECT(store));
1539 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1540 renderer = gtk_cell_renderer_text_new();
1541 g_object_set (renderer, "ypad", 0, NULL);
1542 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1543 -1, "Name", renderer,
1545 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1547 gtk_tree_view_column_set_cell_data_func(column,
1553 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1554 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1555 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1556 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1557 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1558 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1559 gtk_widget_modify_font(tree_view, user_font_get_regular());
1560 remember_ptree_widget(tree_view);
1562 *tree_view_p = tree_view;
1567 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1570 for(i=0; i < num_tree_types; i++) {
1571 tree_is_expanded[i] = TRUE;
1573 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1576 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1579 for(i=0; i < num_tree_types; i++) {
1580 tree_is_expanded[i] = FALSE;
1582 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1586 struct proto_tree_draw_info {
1587 GtkTreeView *tree_view;
1592 main_proto_tree_draw(proto_tree *protocol_tree)
1594 proto_tree_draw(protocol_tree, tree_view);
1599 tree_view_follow_link(field_info *fi)
1603 if(fi->hfinfo->type == FT_FRAMENUM) {
1604 cf_goto_frame(&cfile, fi->value.value.uinteger);
1606 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1607 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1608 browser_open_url(url);
1614 /* If the user selected a position in the tree view, try to find
1615 * the item in the GUI proto_tree that corresponds to that byte, and
1618 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1620 GtkTreeSelection *sel;
1623 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1624 (gint) (((GdkEventButton *)event)->x),
1625 (gint) (((GdkEventButton *)event)->y),
1626 &path, NULL, NULL, NULL))
1628 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1630 /* if that's a doubleclick, try to follow the link */
1631 if(event->type == GDK_2BUTTON_PRESS) {
1632 GtkTreeModel *model;
1636 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1637 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1638 tree_view_follow_link(fi);
1641 else if (((GdkEventButton *)event)->button != 1) {
1642 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1643 gtk_tree_selection_select_path(sel, path);
1651 /* fill the whole protocol tree with the string values */
1653 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1655 GtkTreeStore *store;
1656 struct proto_tree_draw_info info;
1658 info.tree_view = GTK_TREE_VIEW(tree_view);
1661 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1664 * Clear out any crud left over in the display of the protocol
1665 * tree, by removing all nodes from the tree.
1666 * This is how it's done in testgtk.c in GTK+.
1668 gtk_tree_store_clear(store);
1670 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1674 /* fill a single protocol tree item with the string value */
1676 proto_tree_draw_node(proto_node *node, gpointer data)
1678 struct proto_tree_draw_info info;
1679 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1681 field_info *fi = PITEM_FINFO(node);
1682 gchar label_str[ITEM_LABEL_LENGTH];
1684 gboolean is_leaf, is_expanded;
1685 GtkTreeStore *store;
1689 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
1692 /* was a free format label produced? */
1694 label_ptr = fi->rep->representation;
1696 else { /* no, make a generic label */
1697 label_ptr = label_str;
1698 proto_item_fill_label(fi, label_str);
1701 if (node->first_child != NULL) {
1703 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1704 if (tree_is_expanded[fi->tree_type]) {
1708 is_expanded = FALSE;
1713 is_expanded = FALSE;
1716 if (PROTO_ITEM_IS_GENERATED(node)) {
1717 if (PROTO_ITEM_IS_HIDDEN(node)) {
1718 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
1720 label_ptr = g_strdup_printf("[%s]", label_ptr);
1722 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
1723 label_ptr = g_strdup_printf("<%s>", label_ptr);
1726 info.tree_view = parent_info->tree_view;
1727 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1728 gtk_tree_store_append(store, &iter, parent_info->iter);
1729 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1731 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
1737 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1738 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1740 gtk_tree_view_expand_to_path(info.tree_view, path);
1742 gtk_tree_view_collapse_row(info.tree_view, path);
1743 gtk_tree_path_free(path);
1748 * Clear the hex dump and protocol tree panes.
1751 clear_tree_and_hex_views(void)
1753 /* Clear the hex dump by getting rid of all the byte views. */
1754 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1755 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1757 /* Add a placeholder byte view so that there's at least something
1758 displayed in the byte view notebook. */
1759 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1761 /* Clear the protocol tree by removing all nodes in the ctree.
1762 This is how it's done in testgtk.c in GTK+ */
1763 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));