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"
73 #include "gtk/recent.h"
76 #include <gdk/gdkwin32.h>
78 #include "file_dlg_win32.h"
82 #define BYTE_VIEW_WIDTH 16
83 #define BYTE_VIEW_SEP 8
85 #define BIT_VIEW_WIDTH 8
87 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
88 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
89 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
90 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
91 #define E_BYTE_VIEW_START_KEY "byte_view_start"
92 #define E_BYTE_VIEW_END_KEY "byte_view_end"
93 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
94 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
95 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
98 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
99 proto_tree *tree, GtkWidget *tree_view);
102 proto_tree_draw_node(proto_node *node, gpointer data);
104 /* Get the current text window for the notebook. */
106 get_notebook_bv_ptr(GtkWidget *nb_ptr)
111 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
112 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
114 return GTK_BIN(bv_page)->child;
120 * Get the data and length for a byte view, given the byte view page.
121 * Return the pointer, or NULL on error, and set "*data_len" to the length.
124 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
126 tvbuff_t *byte_view_tvb;
127 const guint8 *data_ptr;
129 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
130 if (byte_view_tvb == NULL)
133 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
134 *data_len = tvb_length(byte_view_tvb);
139 * Set the current text window for the notebook to the window that
140 * refers to a particular tvbuff.
143 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
146 GtkWidget *bv_page, *bv;
150 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
152 bv = GTK_BIN(bv_page)->child;
153 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
156 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
162 /* Redraw a given byte view window. */
164 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
170 bv = get_notebook_bv_ptr(nb);
172 data = get_byte_view_data_and_length(bv, &len);
174 packet_hex_print(bv, data, fd, finfo, len);
178 /* Redraw all byte view windows. */
180 redraw_packet_bytes_all(void)
182 if (cfile.current_frame != NULL)
183 redraw_packet_bytes( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
185 redraw_packet_bytes_packet_wins();
187 /* XXX - this is a hack, to workaround a bug in GTK2.x!
188 when changing the font size, even refilling of the corresponding
189 gtk_text_buffer doesn't seem to trigger an update.
190 The only workaround is to freshly select the frame, which will remove any
191 existing notebook tabs and "restart" the whole byte view again. */
192 if (cfile.current_frame != NULL) {
193 cfile.current_row = -1;
194 cf_goto_frame(&cfile, cfile.current_frame->num);
199 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
200 GtkTreePath *path _U_, gpointer user_data _U_)
205 model = gtk_tree_view_get_model(tree_view);
206 gtk_tree_model_get(model, iter, 1, &finfo, -1);
210 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
211 * are thus presumably leaf nodes and cannot be expanded.
213 if (finfo->tree_type != -1) {
214 g_assert(finfo->tree_type >= 0 &&
215 finfo->tree_type < num_tree_types);
216 tree_is_expanded[finfo->tree_type] = TRUE;
221 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
222 GtkTreePath *path _U_, gpointer user_data _U_)
227 model = gtk_tree_view_get_model(tree_view);
228 gtk_tree_model_get(model, iter, 1, &finfo, -1);
232 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
233 * are thus presumably leaf nodes and cannot be collapsed.
235 if (finfo->tree_type != -1) {
236 g_assert(finfo->tree_type >= 0 &&
237 finfo->tree_type < num_tree_types);
238 tree_is_expanded[finfo->tree_type] = FALSE;
242 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
243 #define BYTES_PER_LINE 16 /* max byte values in a line */
244 #define BITS_PER_LINE 8 /* max bit values in a line */
245 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
246 /* max number of characters hex dump takes -
247 2 digits plus trailing blank
248 plus separator between first and
250 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
251 /* number of characters those bytes take;
252 3 characters per byte of hex dump,
253 2 blanks separating hex from ASCII,
254 1 character per byte of ASCII dump */
255 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
256 /* number of characters per line;
257 offset, 2 blanks separating offset
258 from data dump, data dump */
259 #define MAX_LINES 100
260 #define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
262 /* Which byte the offset is referring to. Associates
263 * whitespace with the preceding digits. */
265 byte_num(int offset, int start_point)
267 return (offset - start_point) / 3;
270 bit_num(int offset, int start_point)
272 return (offset - start_point) / 9;
275 struct field_lookup_info {
281 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
285 struct field_lookup_info *fli = (struct field_lookup_info *)data;
287 gtk_tree_model_get(model, iter, 1, &fi, -1);
295 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
297 struct field_lookup_info fli;
299 g_assert(finfo != NULL);
301 model = gtk_tree_view_get_model(tree_view);
303 gtk_tree_model_foreach(model, lookup_finfo, &fli);
305 return gtk_tree_model_get_path(model, &fli.iter);
309 hex_view_get_byte(guint ndigits, int row, int column)
322 * The column of the first hex digit in the first half.
323 * That starts after "ndigits" digits of offset and two
326 digits_start_1 = ndigits + 2;
329 * The column of the last hex digit in the first half.
330 * There are BYTES_PER_LINE/2 bytes displayed in the first
331 * half; there are 2 characters per byte, plus a separating
332 * blank after all but the last byte's characters.
334 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
335 (BYTES_PER_LINE/2 - 1);
338 * The column of the first hex digit in the second half.
339 * Add 2 for the 2 separating blanks between the halves.
341 digits_start_2 = digits_end_1 + 2;
344 * The column of the last hex digit in the second half.
345 * Add the same value we used to get "digits_end_1" from
348 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
349 (BYTES_PER_LINE/2 - 1);
352 * The column of the first "text dump" character in the first half.
353 * Add 3 for the 3 separating blanks between the hex and text dump.
355 text_start_1 = digits_end_2 + 3;
358 * The column of the last "text dump" character in the first half.
359 * There are BYTES_PER_LINE/2 bytes displayed in the first
360 * half; there is 1 character per byte.
362 * Then subtract 1 to get the last column of the first half
363 * rather than the first column after the first half.
365 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
368 * The column of the first "text dump" character in the second half.
369 * Add back the 1 to get the first column after the first half,
370 * and then add 1 for the separating blank between the halves.
372 text_start_2 = text_end_1 + 2;
375 * The column of the last "text dump" character in second half.
376 * Add the same value we used to get "text_end_1" from
379 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
381 /* Given the column and row, determine which byte offset
382 * the user clicked on. */
383 if (column >= digits_start_1 && column <= digits_end_1) {
384 byte = byte_num(column, digits_start_1);
389 else if (column >= digits_start_2 && column <= digits_end_2) {
390 byte = byte_num(column, digits_start_2);
396 else if (column >= text_start_1 && column <= text_end_1) {
397 byte = column - text_start_1;
399 else if (column >= text_start_2 && column <= text_end_2) {
400 byte = 8 + column - text_start_2;
403 /* The user didn't select a hex digit or
404 * text-dump character. */
408 /* Add the number of bytes from the previous rows. */
409 byte += row * BYTES_PER_LINE;
415 bit_view_get_byte(guint ndigits, int row, int column)
424 * The column of the first bit digit.
425 * That starts after "ndigits" digits of offset and two
428 digits_start = ndigits + 2;
431 * The column of the last bit digit.
432 * There are BITS_PER_LINE bytes displayed; there are
433 * 8 characters per byte, plus a separating blank
434 * after all but the last byte's characters.
436 digits_end = digits_start + (BITS_PER_LINE)*8 +
440 * The column of the first "text dump" character.
441 * Add 3 for the 3 separating blanks between the bit and text dump.
443 text_start = digits_end + 3;
446 * The column of the last "text dump" character.
447 * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
449 * Then subtract 1 to get the last column.
451 text_end = text_start + BITS_PER_LINE - 1;
453 /* Given the column and row, determine which byte offset
454 * the user clicked on. */
455 if (column >= digits_start && column <= digits_end) {
456 byte = bit_num(column, digits_start);
461 else if (column >= text_start && column <= text_end) {
462 byte = column - text_start;
465 /* The user didn't select a hex digit or
466 * text-dump character. */
470 /* Add the number of bytes from the previous rows. */
471 byte += row * BITS_PER_LINE;
476 /* If the user selected a certain byte in the byte view, try to find
477 * the item in the GUI proto_tree that corresponds to that byte, and:
479 * if we succeed, select it, and return TRUE;
480 * if we fail, return FALSE. */
482 byte_view_select(GtkWidget *widget, GdkEventButton *event)
484 GtkTextView *bv = GTK_TEXT_VIEW(widget);
486 GtkTreeView *tree_view;
494 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
497 * Somebody clicked on the dummy byte view; do nothing.
501 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
502 E_BYTE_VIEW_TREE_VIEW_PTR));
504 /* get the row/column selected */
505 gtk_text_view_window_to_buffer_coords(bv,
506 gtk_text_view_get_window_type(bv, event->window),
507 (gint) event->x, (gint) event->y, &x, &y);
508 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
509 row = gtk_text_iter_get_line(&iter);
510 column = gtk_text_iter_get_line_offset(&iter);
513 * Get the number of digits of offset being displayed, and
514 * compute the byte position in the buffer.
516 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
518 switch (recent.gui_bytes_view) {
520 byte = hex_view_get_byte(ndigits, row, column);
523 byte = bit_view_get_byte(ndigits, row, column);
526 g_assert_not_reached();
533 /* Get the data source tvbuff */
534 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
536 return highlight_field(tvb, byte, tree_view, tree);
539 /* This highlights the field in the proto tree that is at position byte */
541 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
545 GtkTreePath *first_path, *path;
547 struct field_lookup_info fli;
550 /* Find the finfo that corresponds to our byte. */
551 finfo = proto_find_field_from_offset(tree, byte, tvb);
557 model = gtk_tree_view_get_model(tree_view);
559 gtk_tree_model_foreach(model, lookup_finfo, &fli);
561 /* Expand our field's row */
562 first_path = gtk_tree_model_get_path(model, &fli.iter);
563 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
564 expand_tree(tree_view, &fli.iter, NULL, NULL);
566 /* ... and its parents */
567 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
568 path = gtk_tree_model_get_path(model, &parent);
569 gtk_tree_view_expand_row(tree_view, path, FALSE);
570 expand_tree(tree_view, &parent, NULL, NULL);
572 gtk_tree_path_free(path);
575 /* select our field's row */
576 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
579 /* And position the window so the selection is visible.
580 * Position the selection in the middle of the viewable
582 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
584 gtk_tree_path_free(first_path);
589 /* Calls functions for different mouse-button presses. */
591 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
593 GdkEventButton *event_button = NULL;
595 if(widget == NULL || event == NULL || data == NULL) {
599 if(event->type == GDK_BUTTON_PRESS) {
600 event_button = (GdkEventButton *) event;
602 /* To qoute the "Gdk Event Structures" doc:
603 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
604 switch(event_button->button) {
607 return byte_view_select(widget, event_button);
609 return popup_menu_handler(widget, event, data);
623 byte_nb = gtk_notebook_new();
624 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
626 /* this will only have an effect, if no tabs are shown */
627 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
629 /* set the tabs scrollable, if they don't fit into the pane */
630 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
632 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
633 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
635 /* Add a placeholder byte view so that there's at least something
636 displayed in the byte view notebook. */
637 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
643 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
645 const guint8 *byte_data;
648 byte_data = get_byte_view_data_and_length(bv, &byte_len);
649 if (byte_data == NULL) {
650 /* This must be the dummy byte view if no packet is selected. */
653 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
657 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
658 proto_tree *tree, GtkWidget *tree_view)
660 GtkWidget *byte_view, *byte_scrollw, *label;
664 /* Byte view. Create a scrolled window for the text. */
665 byte_scrollw = scrolled_window_new(NULL, NULL);
666 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
668 /* Add scrolled pane to tabbed window */
669 label = gtk_label_new(name);
670 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
672 gtk_widget_show(byte_scrollw);
674 byte_view = gtk_text_view_new();
675 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
676 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
677 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
679 #ifdef NEW_PACKET_LIST
680 style = gtk_widget_get_style(GTK_WIDGET(top_level));
682 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
684 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
685 gtk_text_buffer_create_tag(buf, "reverse",
686 "font-desc", user_font_get_regular(),
687 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
688 "background-gdk", &style->base[GTK_STATE_SELECTED],
691 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
692 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
693 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
695 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
696 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
697 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
699 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
700 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
702 gtk_widget_show(byte_view);
704 /* no tabs if this is the first page */
705 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
706 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
708 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
710 /* set this page (this will print the packet data) */
711 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
712 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
718 add_main_byte_views(epan_dissect_t *edt)
720 add_byte_views(edt, tree_view, byte_nb_ptr);
724 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
725 GtkWidget *byte_nb_ptr)
731 * Get rid of all the old notebook tabs.
733 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
734 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
737 * Add to the specified byte view notebook tabs for hex dumps
738 * of all the data sources for the specified frame.
740 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
742 add_byte_tab(byte_nb_ptr, get_data_source_name(src), src->tvb, edt->tree,
747 * Initially select the first byte view.
749 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
754 static GtkWidget *savehex_dlg=NULL;
757 savehex_dlg_destroy_cb(void)
763 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
765 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
767 gboolean end_of_line = TRUE; /* Initial state is end of line */
768 int byte_line_part_length;
773 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
774 hex_str = g_string_new("");
775 char_str= g_string_new("");
780 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
783 g_string_append_printf(hex_str," %02x",*data_p);
785 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
790 /* Look ahead to see if this is the end of the data */
791 byte_line_part_length = (++i) % byte_line_length;
793 /* End of data - need to fill in spaces in hex string and then do "end of line".
796 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
797 g_string_append(hex_str," "); /* Three spaces for each missing byte */
801 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
807 g_string_append(copy_buffer, hex_str->str);
809 /* Two spaces between hex and text */
810 g_string_append_c(copy_buffer, ' ');
811 g_string_append_c(copy_buffer, ' ');
812 g_string_append(copy_buffer, char_str->str);
814 /* Setup ready for next line */
815 g_string_assign(char_str,"");
816 g_string_assign(hex_str, "\n");
820 g_string_free(hex_str, TRUE);
821 g_string_free(char_str, TRUE);
825 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
830 if(isprint(*data_p)) {
832 } else if(*data_p==0x0a) {
833 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
835 return 1; /* Just ignore non-printable bytes */
837 g_string_append_c(copy_buffer,to_append);
842 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
844 g_string_append_printf(copy_buffer, "%02x", *data_p);
849 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
854 int bytes_consumed = 0;
857 const guint8* data_p;
859 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
861 bv = get_notebook_bv_ptr(byte_nb_ptr);
863 /* shouldn't happen */
864 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
868 data_p = get_byte_view_data_and_length(bv, &len);
869 g_assert(data_p != NULL);
871 flags = data_type & CD_FLAGSMASK;
872 data_type = data_type & CD_TYPEMASK;
874 if(flags & CD_FLAGS_SELECTEDONLY) {
877 /* Get the start and end of the highlighted bytes.
878 * XXX The keys appear to be REVERSED start <-> end throughout this file!
879 * Should this be fixed? There is one exception - packet_hex_reprint,
880 * so can't just change it round without functional change.
882 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
883 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
885 if(start >= 0 && end > start && (end - start <= (int)len)) {
893 /* This is too different from other text formats - handle separately */
894 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
897 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
898 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
901 /* Completely different logic to text copies - leave copy buffer alone */
902 copy_binary_to_clipboard(data_p,len);
905 /* Incrementally write to text buffer in various formats */
909 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
912 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
915 g_assert_not_reached();
919 g_assert(bytes_consumed>0);
920 data_p += bytes_consumed;
921 len -= bytes_consumed;
926 if(copy_buffer->len > 0) {
927 copy_to_clipboard(copy_buffer);
930 g_string_free(copy_buffer, TRUE);
933 /* save the current highlighted hex data */
935 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
940 const guint8 *data_p = NULL;
943 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
945 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
946 if (!file ||! *file) {
947 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
952 if (test_for_directory(file) == EISDIR) {
953 /* It's a directory - set the file selection box to display that
954 directory, and leave the selection box displayed. */
955 set_last_open_dir(file);
957 file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
958 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
959 return FALSE; /* do gtk_dialog_run again */
962 /* XXX: Must check if file name exists first */
964 bv = get_notebook_bv_ptr(byte_nb_ptr);
966 /* shouldn't happen */
967 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
972 * Retrieve the info we need
974 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
975 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
976 data_p = get_byte_view_data_and_length(bv, &len);
978 if (data_p == NULL || start == -1 || start > end) {
979 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
980 "No data selected to save!");
985 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
987 open_failure_alert_box(file, errno, TRUE);
991 if (ws_write(fd, data_p + start, end - start) < 0) {
992 write_failure_alert_box(file, errno);
997 if (ws_close(fd) < 0) {
998 write_failure_alert_box(file, errno);
1003 /* Get rid of the dialog box */
1005 #if 0 /* being handled by caller (for now) */
1006 window_destroy(GTK_WIDGET(savehex_dlg));
1011 /* Launch the dialog box to put up the file selection box etc */
1012 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1016 const guint8 *data_p = NULL;
1023 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
1027 /* don't show up the dialog, if no data has to be saved */
1028 bv = get_notebook_bv_ptr(byte_nb_ptr);
1030 /* shouldn't happen */
1031 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1034 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1035 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1036 data_p = get_byte_view_data_and_length(bv, &len);
1038 if (data_p == NULL || start == -1 || start > end) {
1039 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1043 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
1044 /* if the window is already open, bring it to front */
1046 reactivate_window(savehex_dlg);
1051 * Build the dialog box we need.
1053 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1054 #if GTK_CHECK_VERSION(2,8,0)
1055 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
1059 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1060 end - start, plurality(end - start, "byte", "bytes"));
1061 dlg_lb = gtk_label_new(label);
1063 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1064 gtk_widget_show(dlg_lb);
1066 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
1069 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1070 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1072 window_destroy(savehex_dlg);
1075 /* "Run" the GtkFileChooserDialog. */
1076 /* Upon exit: If "Accept" run the OK callback. */
1077 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
1078 /* If not accept (ie: cancel) destroy the window. */
1079 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
1080 /* return with a TRUE status so that the dialog window will be destroyed. */
1081 /* Trying to re-run the dialog after popping up an alert box will not work */
1082 /* since the user will not be able to dismiss the alert box. */
1083 /* The (somewhat unfriendly) effect: the user must re-invoke the */
1084 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
1086 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
1087 /* GtkFileChooserDialog. */
1088 while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1089 if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
1090 break; /* we're done */
1093 window_destroy(savehex_dlg);
1098 /* Update the progress bar this many times when reading a file. */
1099 #define N_PROGBAR_UPDATES 100
1100 /* The minimum packet length required to check if a progres bar is needed or not */
1101 #define MIN_PACKET_LENGTH 1024
1104 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1105 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1106 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1107 * presumably why there's a progress bar for it.)
1109 * Perhaps what's needed is a custom widget (either one that lets you stuff
1110 * text into it more quickly, or one that's a "virtual" widget so that the
1111 * text for a row is constructed, via a callback, when the row is to be
1112 * displayed). A custom widget might also let us treat the offset, hex
1113 * data, and ASCII data as three columns, so you can select purely in
1114 * the hex dump column.
1117 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1118 int bend, int astart, int aend, int encoding)
1120 int i = 0, j, k = 0, b, cur;
1121 guchar line[MAX_LINES_LEN + 1];
1122 static guchar hexchars[16] = {
1123 '0', '1', '2', '3', '4', '5', '6', '7',
1124 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1125 static const guint8 bitmask[8] = {
1126 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1128 unsigned int use_digits;
1129 gboolean reverse, newreverse;
1130 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1131 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1133 const char *revstyle;
1134 GtkTextMark *mark = NULL;
1136 progdlg_t *progbar = NULL;
1138 gboolean progbar_stop_flag;
1139 GTimeVal progbar_start_time;
1140 gchar progbar_status_str[100];
1141 int progbar_nextstep;
1142 int progbar_quantum;
1144 gtk_text_buffer_set_text(buf, "", 0);
1145 gtk_text_buffer_get_start_iter(buf, &iter);
1147 gtk_text_view_set_buffer( bv_text_view, NULL);
1150 * How many of the leading digits of the offset will we supply?
1151 * We always supply at least 4 digits, but if the maximum offset
1152 * won't fit in 4 digits, we use as many digits as will be needed.
1154 if (((len - 1) & 0xF0000000) != 0)
1155 use_digits = 8; /* need all 8 digits */
1156 else if (((len - 1) & 0x0F000000) != 0)
1157 use_digits = 7; /* need 7 digits */
1158 else if (((len - 1) & 0x00F00000) != 0)
1159 use_digits = 6; /* need 6 digits */
1160 else if (((len - 1) & 0x000F0000) != 0)
1161 use_digits = 5; /* need 5 digits */
1163 use_digits = 4; /* we'll supply 4 digits */
1165 /* Record the number of digits in this text view. */
1166 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1168 /* Update the progress bar when it gets to this value. */
1169 if (len > MIN_PACKET_LENGTH){
1170 progbar_nextstep = 0;
1172 /* If length =< MIN_PACKET_LENGTH
1173 * there is no need to calculate the progress
1175 progbar_nextstep = len+1;
1178 /* When we reach the value that triggers a progress bar update,
1179 bump that value by this amount. */
1180 progbar_quantum = len/N_PROGBAR_UPDATES;
1181 /* Progress so far. */
1184 progbar_stop_flag = FALSE;
1185 g_get_current_time(&progbar_start_time);
1189 /* Create the progress bar if necessary.
1190 We check on every iteration of the loop, so that it takes no
1191 longer than the standard time to create it (otherwise, for a
1192 large packet, we might take considerably longer than that standard
1193 time in order to get to the next progress bar step). */
1194 if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1195 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1198 &progbar_start_time,
1201 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1202 when we update it, we have to run the GTK+ main loop to get it
1203 to repaint what's pending, and doing so may involve an "ioctl()"
1204 to see if there's any pending input from an X server, and doing
1205 that for every packet can be costly, especially on a big file. */
1206 if (i >= progbar_nextstep) {
1208 if (progbar != NULL) {
1209 /* let's not divide by zero. I should never be started
1210 * with count == 0, so let's assert that
1213 progbar_val = (gfloat) i / len;
1214 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1215 "%4u of %u bytes", i, len);
1216 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1219 progbar_nextstep += progbar_quantum;
1222 if (progbar_stop_flag) {
1223 /* Well, the user decided to abort the operation. Just stop,
1224 and arrange to return TRUE to our caller, so they know it
1225 was stopped explicitly. */
1229 /* Print the line number */
1233 c = (i >> (j*4)) & 0xF;
1234 line[cur++] = hexchars[c];
1239 /* Display with inverse video ? */
1240 if (prefs.gui_hex_dump_highlight_style)
1241 revstyle = "reverse";
1245 /* Do we start in reverse? */
1246 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1248 switch (recent.gui_bytes_view) {
1250 k = i + BYTE_VIEW_WIDTH;
1253 k = i + BIT_VIEW_WIDTH;
1256 g_assert_not_reached();
1259 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1263 /* Print the hex bit */
1266 switch (recent.gui_bytes_view) {
1268 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1269 line[cur++] = hexchars[pd[i] & 0x0f];
1272 for (b = 0; b < 8; b++) {
1273 line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1277 g_assert_not_reached();
1280 switch (recent.gui_bytes_view) {
1282 line[cur++] = ' '; line[cur++] = ' ';
1285 for (b = 0; b < 8; b++) {
1290 g_assert_not_reached();
1294 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1295 /* Have we gone from reverse to plain? */
1296 if (reverse && (reverse != newreverse)) {
1297 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1301 /* Inter byte space if not at end of line */
1304 /* insert a space every BYTE_VIEW_SEP bytes */
1305 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1309 /* Have we gone from plain to reversed? */
1310 if (!reverse && (reverse != newreverse)) {
1311 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1313 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1316 reverse = newreverse;
1319 /* Print remaining part of line */
1320 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1325 /* Print some space at the end of the line */
1326 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1328 /* Print the ASCII bit */
1330 /* Do we start in reverse? */
1331 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1333 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1340 if (encoding == CHAR_ASCII) {
1343 else if (encoding == CHAR_EBCDIC) {
1344 c = EBCDIC_to_ASCII1(pd[i]);
1347 g_assert_not_reached();
1349 line[cur++] = isprint(c) ? c : '.';
1354 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1355 /* Have we gone from reverse to plain? */
1356 if (reverse && (reverse != newreverse)) {
1357 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1363 /* insert a space every BYTE_VIEW_SEP bytes */
1364 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1368 /* Have we gone from plain to reversed? */
1369 if (!reverse && (reverse != newreverse)) {
1370 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1374 reverse = newreverse;
1376 /* Print remaining part of line */
1378 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1383 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1384 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1390 /* We're done printing the packets; destroy the progress bar if
1392 if (progbar != NULL)
1393 destroy_progress_dlg(progbar);
1395 /* scroll text into position */
1397 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1400 gtk_text_view_set_buffer( bv_text_view, buf);
1403 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1404 gtk_text_buffer_delete_mark(buf, mark);
1406 g_object_unref(buf);
1410 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1411 field_info *finfo, guint len)
1413 /* do the initial printing and save the information needed */
1414 /* to redraw the display if preferences change. */
1416 int bstart = -1, bend = -1, blen = -1;
1417 int astart = -1, aend = -1, alen = -1;
1419 if (finfo != NULL) {
1420 bstart = finfo->start;
1421 blen = finfo->length;
1422 astart = finfo->appendix_start;
1423 alen = finfo->appendix_length;
1425 if (bstart >= 0 && blen >= 0) {
1426 bend = bstart + blen;
1428 if (astart >= 0 && alen >= 0) {
1429 aend = astart + alen;
1432 /* save the information needed to redraw the text */
1433 /* should we save the fd & finfo pointers instead ?? */
1434 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1435 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1436 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1437 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1438 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1439 GUINT_TO_POINTER((guint)fd->flags.encoding));
1441 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1445 * Redraw the text using the saved information; usually called if
1446 * the preferences have changed.
1449 packet_hex_reprint(GtkWidget *bv)
1451 int start, end, encoding;
1456 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1457 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1458 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1459 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1460 data = get_byte_view_data_and_length(bv, &len);
1461 g_assert(data != NULL);
1462 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1464 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1467 /* List of all protocol tree widgets, so we can globally set the selection
1468 mode and font of all of them. */
1469 static GList *ptree_widgets;
1471 /* Add a protocol tree widget to the list of protocol tree widgets. */
1472 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1475 remember_ptree_widget(GtkWidget *ptreew)
1477 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1479 /* Catch the "destroy" event on the widget, so that we remove it from
1480 the list when it's destroyed. */
1481 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1484 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1486 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1488 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1491 /* Set the selection mode of a given packet tree window. */
1493 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1495 GtkTreeSelection *selection;
1497 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1498 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1499 I think "browse" in Wireshark makes more sense than "SINGLE" in
1502 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1505 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1510 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1512 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1515 /* Set the selection mode of all packet tree windows. */
1517 set_ptree_sel_browse_all(gboolean val)
1519 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1523 set_ptree_font_cb(gpointer data, gpointer user_data)
1525 gtk_widget_modify_font((GtkWidget *)data,
1526 (PangoFontDescription *)user_data);
1530 set_ptree_font_all(PangoFontDescription *font)
1532 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1537 * Each expert_color_* level below should match the light gradient
1538 * colors in image/expert_indicators.svg.
1540 gboolean colors_ok = FALSE;
1541 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1542 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1543 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1544 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1545 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1546 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1548 gchar *expert_color_chat_str;
1549 gchar *expert_color_note_str;
1550 gchar *expert_color_warn_str;
1551 gchar *expert_color_error_str;
1552 gchar *expert_color_foreground_str;
1554 void proto_draw_colors_init(void)
1560 get_color(&expert_color_chat);
1561 get_color(&expert_color_note);
1562 get_color(&expert_color_warn);
1563 get_color(&expert_color_error);
1564 get_color(&expert_color_foreground);
1565 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1566 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1567 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1568 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1569 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1571 get_color(&hidden_proto_item);
1577 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1578 GtkCellRenderer *cell,
1579 GtkTreeModel *tree_model,
1585 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1588 proto_draw_colors_init();
1591 /* for the various possible attributes, see:
1592 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1594 * color definitions can be found at:
1595 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1596 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1599 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1600 * weight/style: doesn't take any effect
1603 /* for each field, we have to reset the renderer attributes */
1604 g_object_set (cell, "foreground-set", FALSE, NULL);
1606 g_object_set (cell, "background-set", FALSE, NULL);
1608 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1609 g_object_set (cell, "underline-set", FALSE, NULL);
1611 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1612 g_object_set (cell, "style-set", FALSE, NULL);*/
1614 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1615 g_object_set (cell, "weight-set", FALSE, NULL);*/
1617 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1618 /* we use "[...]" to mark generated items, no need to change things here */
1620 /* as some fonts don't support italic, don't use this */
1621 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1622 g_object_set (cell, "style-set", TRUE, NULL);
1624 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1625 g_object_set (cell, "weight-set", TRUE, NULL);*/
1628 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1629 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1630 g_object_set (cell, "foreground-set", TRUE, NULL);
1633 if (fi && fi->hfinfo) {
1634 if(fi->hfinfo->type == FT_PROTOCOL) {
1635 g_object_set (cell, "background", "gray90", NULL);
1636 g_object_set (cell, "background-set", TRUE, NULL);
1637 g_object_set (cell, "foreground", "black", NULL);
1638 g_object_set (cell, "foreground-set", TRUE, NULL);
1639 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1640 g_object_set (cell, "weight-set", TRUE, NULL);*/
1643 if((fi->hfinfo->type == FT_FRAMENUM) ||
1644 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1645 render_as_url(cell);
1649 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1650 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1652 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1653 g_object_set (cell, "background-set", TRUE, NULL);
1656 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1657 g_object_set (cell, "background-set", TRUE, NULL);
1660 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1661 g_object_set (cell, "background-set", TRUE, NULL);
1664 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1665 g_object_set (cell, "background-set", TRUE, NULL);
1668 g_assert_not_reached();
1670 g_object_set (cell, "foreground", "black", NULL);
1671 g_object_set (cell, "foreground-set", TRUE, NULL);
1676 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1678 GtkWidget *tv_scrollw, *tree_view;
1679 GtkTreeStore *store;
1680 GtkCellRenderer *renderer;
1681 GtkTreeViewColumn *column;
1685 tv_scrollw = scrolled_window_new(NULL, NULL);
1686 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1689 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1690 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1691 g_object_unref(G_OBJECT(store));
1692 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1693 renderer = gtk_cell_renderer_text_new();
1694 g_object_set (renderer, "ypad", 0, NULL);
1695 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1696 -1, "Name", renderer,
1698 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1700 gtk_tree_view_column_set_cell_data_func(column,
1706 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1707 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1708 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1709 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1710 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1711 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1712 gtk_widget_modify_font(tree_view, user_font_get_regular());
1713 remember_ptree_widget(tree_view);
1715 *tree_view_p = tree_view;
1720 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1723 for(i=0; i < num_tree_types; i++) {
1724 tree_is_expanded[i] = TRUE;
1726 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1729 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1732 for(i=0; i < num_tree_types; i++) {
1733 tree_is_expanded[i] = FALSE;
1735 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1739 struct proto_tree_draw_info {
1740 GtkTreeView *tree_view;
1745 main_proto_tree_draw(proto_tree *protocol_tree)
1747 proto_tree_draw(protocol_tree, tree_view);
1752 tree_view_follow_link(field_info *fi)
1756 if(fi->hfinfo->type == FT_FRAMENUM) {
1757 cf_goto_frame(&cfile, fi->value.value.uinteger);
1759 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1760 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1761 browser_open_url(url);
1767 /* If the user selected a position in the tree view, try to find
1768 * the item in the GUI proto_tree that corresponds to that byte, and
1771 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1773 GtkTreeSelection *sel;
1776 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1777 (gint) (((GdkEventButton *)event)->x),
1778 (gint) (((GdkEventButton *)event)->y),
1779 &path, NULL, NULL, NULL))
1781 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1783 /* if that's a doubleclick, try to follow the link */
1784 if(event->type == GDK_2BUTTON_PRESS) {
1785 GtkTreeModel *model;
1789 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1790 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1791 tree_view_follow_link(fi);
1794 else if (((GdkEventButton *)event)->button != 1) {
1795 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1796 gtk_tree_selection_select_path(sel, path);
1804 /* fill the whole protocol tree with the string values */
1806 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1808 GtkTreeStore *store;
1809 struct proto_tree_draw_info info;
1811 info.tree_view = GTK_TREE_VIEW(tree_view);
1814 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1817 * Clear out any crud left over in the display of the protocol
1818 * tree, by removing all nodes from the tree.
1819 * This is how it's done in testgtk.c in GTK+.
1821 gtk_tree_store_clear(store);
1823 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1827 /* fill a single protocol tree item with the string value */
1829 proto_tree_draw_node(proto_node *node, gpointer data)
1831 struct proto_tree_draw_info info;
1832 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1834 field_info *fi = PNODE_FINFO(node);
1835 gchar label_str[ITEM_LABEL_LENGTH];
1837 gboolean is_leaf, is_expanded;
1838 GtkTreeStore *store;
1842 g_assert(fi && "dissection with an invisible proto tree?");
1844 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
1847 /* was a free format label produced? */
1849 label_ptr = fi->rep->representation;
1851 else { /* no, make a generic label */
1852 label_ptr = label_str;
1853 proto_item_fill_label(fi, label_str);
1856 if (node->first_child != NULL) {
1858 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1859 if (tree_is_expanded[fi->tree_type]) {
1863 is_expanded = FALSE;
1868 is_expanded = FALSE;
1871 if (PROTO_ITEM_IS_GENERATED(node)) {
1872 if (PROTO_ITEM_IS_HIDDEN(node)) {
1873 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
1875 label_ptr = g_strdup_printf("[%s]", label_ptr);
1877 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
1878 label_ptr = g_strdup_printf("<%s>", label_ptr);
1881 info.tree_view = parent_info->tree_view;
1882 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1883 gtk_tree_store_append(store, &iter, parent_info->iter);
1884 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1886 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
1892 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1893 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1895 gtk_tree_view_expand_to_path(info.tree_view, path);
1897 gtk_tree_view_collapse_row(info.tree_view, path);
1898 gtk_tree_path_free(path);
1903 * Clear the hex dump and protocol tree panes.
1906 clear_tree_and_hex_views(void)
1908 /* Clear the hex dump by getting rid of all the byte views. */
1909 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1910 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1912 /* Add a placeholder byte view so that there's at least something
1913 displayed in the byte view notebook. */
1914 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1916 /* Clear the protocol tree by removing all nodes in the ctree.
1917 This is how it's done in testgtk.c in GTK+ */
1918 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));
1922 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
1924 if (recent.gui_bytes_view != view) {
1925 recent.gui_bytes_view = view;
1926 redraw_packet_bytes_all();