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 E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
83 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
84 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
85 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
86 #define E_BYTE_VIEW_START_KEY "byte_view_start"
87 #define E_BYTE_VIEW_END_KEY "byte_view_end"
88 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
89 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
90 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
93 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
94 proto_tree *tree, GtkWidget *tree_view);
97 proto_tree_draw_node(proto_node *node, gpointer data);
99 /* Get the current text window for the notebook. */
101 get_notebook_bv_ptr(GtkWidget *nb_ptr)
106 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
107 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
109 return GTK_BIN(bv_page)->child;
115 * Get the data and length for a byte view, given the byte view page.
116 * Return the pointer, or NULL on error, and set "*data_len" to the length.
119 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
121 tvbuff_t *byte_view_tvb;
122 const guint8 *data_ptr;
124 byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
125 if (byte_view_tvb == NULL)
128 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
129 *data_len = tvb_length(byte_view_tvb);
134 * Set the current text window for the notebook to the window that
135 * refers to a particular tvbuff.
138 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
141 GtkWidget *bv_page, *bv;
145 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
147 bv = GTK_BIN(bv_page)->child;
148 bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
151 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
157 /* Redraw a given byte view window. */
159 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
165 bv = get_notebook_bv_ptr(nb);
167 data = get_byte_view_data_and_length(bv, &len);
169 packet_hex_print(bv, data, fd, finfo, len);
173 /* Redraw all byte view windows. */
175 redraw_packet_bytes_all(void)
177 if (cfile.current_frame != NULL)
178 redraw_packet_bytes( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
180 redraw_packet_bytes_packet_wins();
182 /* XXX - this is a hack, to workaround a bug in GTK2.x!
183 when changing the font size, even refilling of the corresponding
184 gtk_text_buffer doesn't seem to trigger an update.
185 The only workaround is to freshly select the frame, which will remove any
186 existing notebook tabs and "restart" the whole byte view again. */
187 if (cfile.current_frame != NULL) {
188 cfile.current_row = -1;
189 cf_goto_frame(&cfile, cfile.current_frame->num);
194 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
195 GtkTreePath *path _U_, gpointer user_data _U_)
200 model = gtk_tree_view_get_model(tree_view);
201 gtk_tree_model_get(model, iter, 1, &finfo, -1);
205 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
206 * are thus presumably leaf nodes and cannot be expanded.
208 if (finfo->tree_type != -1) {
209 g_assert(finfo->tree_type >= 0 &&
210 finfo->tree_type < num_tree_types);
211 tree_is_expanded[finfo->tree_type] = TRUE;
216 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
217 GtkTreePath *path _U_, gpointer user_data _U_)
222 model = gtk_tree_view_get_model(tree_view);
223 gtk_tree_model_get(model, iter, 1, &finfo, -1);
227 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
228 * are thus presumably leaf nodes and cannot be collapsed.
230 if (finfo->tree_type != -1) {
231 g_assert(finfo->tree_type >= 0 &&
232 finfo->tree_type < num_tree_types);
233 tree_is_expanded[finfo->tree_type] = FALSE;
237 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
238 #define BYTES_PER_LINE 16 /* max byte values in a line */
239 #define BITS_PER_LINE 8 /* max bit values in a line */
240 #define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
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 bit_num(int offset, int start_point)
268 return (offset - start_point) / 9;
271 struct field_lookup_info {
277 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
281 struct field_lookup_info *fli = (struct field_lookup_info *)data;
283 gtk_tree_model_get(model, iter, 1, &fi, -1);
291 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
293 struct field_lookup_info fli;
295 g_assert(finfo != NULL);
297 model = gtk_tree_view_get_model(tree_view);
299 gtk_tree_model_foreach(model, lookup_finfo, &fli);
301 return gtk_tree_model_get_path(model, &fli.iter);
305 hex_view_get_byte(guint ndigits, int row, int column)
318 * The column of the first hex digit in the first half.
319 * That starts after "ndigits" digits of offset and two
322 digits_start_1 = ndigits + 2;
325 * The column of the last hex digit in the first half.
326 * There are BYTES_PER_LINE/2 bytes displayed in the first
327 * half; there are 2 characters per byte, plus a separating
328 * blank after all but the last byte's characters.
330 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
331 (BYTES_PER_LINE/2 - 1);
334 * The column of the first hex digit in the second half.
335 * Add 2 for the 2 separating blanks between the halves.
337 digits_start_2 = digits_end_1 + 2;
340 * The column of the last hex digit in the second half.
341 * Add the same value we used to get "digits_end_1" from
344 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
345 (BYTES_PER_LINE/2 - 1);
348 * The column of the first "text dump" character in the first half.
349 * Add 3 for the 3 separating blanks between the hex and text dump.
351 text_start_1 = digits_end_2 + 3;
354 * The column of the last "text dump" character in the first half.
355 * There are BYTES_PER_LINE/2 bytes displayed in the first
356 * half; there is 1 character per byte.
358 * Then subtract 1 to get the last column of the first half
359 * rather than the first column after the first half.
361 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
364 * The column of the first "text dump" character in the second half.
365 * Add back the 1 to get the first column after the first half,
366 * and then add 1 for the separating blank between the halves.
368 text_start_2 = text_end_1 + 2;
371 * The column of the last "text dump" character in second half.
372 * Add the same value we used to get "text_end_1" from
375 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
377 /* Given the column and row, determine which byte offset
378 * the user clicked on. */
379 if (column >= digits_start_1 && column <= digits_end_1) {
380 byte = byte_num(column, digits_start_1);
385 else if (column >= digits_start_2 && column <= digits_end_2) {
386 byte = byte_num(column, digits_start_2);
392 else if (column >= text_start_1 && column <= text_end_1) {
393 byte = column - text_start_1;
395 else if (column >= text_start_2 && column <= text_end_2) {
396 byte = 8 + column - text_start_2;
399 /* The user didn't select a hex digit or
400 * text-dump character. */
404 /* Add the number of bytes from the previous rows. */
405 byte += row * BYTES_PER_LINE;
411 bit_view_get_byte(guint ndigits, int row, int column)
420 * The column of the first bit digit.
421 * That starts after "ndigits" digits of offset and two
424 digits_start = ndigits + 2;
427 * The column of the last bit digit.
428 * There are BITS_PER_LINE bytes displayed; there are
429 * 8 characters per byte, plus a separating blank
430 * after all but the last byte's characters.
432 digits_end = digits_start + (BITS_PER_LINE)*8 +
436 * The column of the first "text dump" character.
437 * Add 3 for the 3 separating blanks between the bit and text dump.
439 text_start = digits_end + 3;
442 * The column of the last "text dump" character.
443 * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
445 * Then subtract 1 to get the last column.
447 text_end = text_start + BITS_PER_LINE - 1;
449 /* Given the column and row, determine which byte offset
450 * the user clicked on. */
451 if (column >= digits_start && column <= digits_end) {
452 byte = bit_num(column, digits_start);
457 else if (column >= text_start && column <= text_end) {
458 byte = column - text_start;
461 /* The user didn't select a hex digit or
462 * text-dump character. */
466 /* Add the number of bytes from the previous rows. */
467 byte += row * BITS_PER_LINE;
472 /* If the user selected a certain byte in the byte view, try to find
473 * the item in the GUI proto_tree that corresponds to that byte, and:
475 * if we succeed, select it, and return TRUE;
476 * if we fail, return FALSE. */
478 byte_view_select(GtkWidget *widget, GdkEventButton *event)
480 GtkTextView *bv = GTK_TEXT_VIEW(widget);
482 GtkTreeView *tree_view;
490 tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
493 * Somebody clicked on the dummy byte view; do nothing.
497 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
498 E_BYTE_VIEW_TREE_VIEW_PTR));
500 /* get the row/column selected */
501 gtk_text_view_window_to_buffer_coords(bv,
502 gtk_text_view_get_window_type(bv, event->window),
503 (gint) event->x, (gint) event->y, &x, &y);
504 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
505 row = gtk_text_iter_get_line(&iter);
506 column = gtk_text_iter_get_line_offset(&iter);
509 * Get the number of digits of offset being displayed, and
510 * compute the byte position in the buffer.
512 ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
514 switch (recent.gui_bytes_view) {
516 byte = hex_view_get_byte(ndigits, row, column);
519 byte = bit_view_get_byte(ndigits, row, column);
522 g_assert_not_reached();
529 /* Get the data source tvbuff */
530 tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
532 return highlight_field(tvb, byte, tree_view, tree);
535 /* This highlights the field in the proto tree that is at position byte */
537 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
541 GtkTreePath *first_path, *path;
543 struct field_lookup_info fli;
546 /* Find the finfo that corresponds to our byte. */
547 finfo = proto_find_field_from_offset(tree, byte, tvb);
553 model = gtk_tree_view_get_model(tree_view);
555 gtk_tree_model_foreach(model, lookup_finfo, &fli);
557 /* Expand our field's row */
558 first_path = gtk_tree_model_get_path(model, &fli.iter);
559 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
560 expand_tree(tree_view, &fli.iter, NULL, NULL);
562 /* ... and its parents */
563 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
564 path = gtk_tree_model_get_path(model, &parent);
565 gtk_tree_view_expand_row(tree_view, path, FALSE);
566 expand_tree(tree_view, &parent, NULL, NULL);
568 gtk_tree_path_free(path);
571 /* select our field's row */
572 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
575 /* And position the window so the selection is visible.
576 * Position the selection in the middle of the viewable
578 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
580 gtk_tree_path_free(first_path);
585 /* Calls functions for different mouse-button presses. */
587 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
589 GdkEventButton *event_button = NULL;
591 if(widget == NULL || event == NULL || data == NULL) {
595 if(event->type == GDK_BUTTON_PRESS) {
596 event_button = (GdkEventButton *) event;
598 /* To qoute the "Gdk Event Structures" doc:
599 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
600 switch(event_button->button) {
603 return byte_view_select(widget, event_button);
605 return popup_menu_handler(widget, event, data);
619 byte_nb = gtk_notebook_new();
620 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
622 /* this will only have an effect, if no tabs are shown */
623 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
625 /* set the tabs scrollable, if they don't fit into the pane */
626 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
628 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
629 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
631 /* Add a placeholder byte view so that there's at least something
632 displayed in the byte view notebook. */
633 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
639 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
641 const guint8 *byte_data;
644 byte_data = get_byte_view_data_and_length(bv, &byte_len);
645 if (byte_data == NULL) {
646 /* This must be the dummy byte view if no packet is selected. */
649 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
653 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
654 proto_tree *tree, GtkWidget *tree_view)
656 GtkWidget *byte_view, *byte_scrollw, *label;
660 /* Byte view. Create a scrolled window for the text. */
661 byte_scrollw = scrolled_window_new(NULL, NULL);
662 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
664 /* Add scrolled pane to tabbed window */
665 label = gtk_label_new(name);
666 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
668 gtk_widget_show(byte_scrollw);
670 byte_view = gtk_text_view_new();
671 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
672 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
673 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
675 #ifdef NEW_PACKET_LIST
676 style = gtk_widget_get_style(GTK_WIDGET(top_level));
678 style = gtk_widget_get_style(GTK_WIDGET(packet_list));
680 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
681 gtk_text_buffer_create_tag(buf, "reverse",
682 "font-desc", user_font_get_regular(),
683 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
684 "background-gdk", &style->base[GTK_STATE_SELECTED],
687 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
688 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
689 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
691 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
692 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
693 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
695 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
696 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
698 gtk_widget_show(byte_view);
700 /* no tabs if this is the first page */
701 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
702 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
704 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
706 /* set this page (this will print the packet data) */
707 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
708 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
714 add_main_byte_views(epan_dissect_t *edt)
716 add_byte_views(edt, tree_view, byte_nb_ptr);
720 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
721 GtkWidget *byte_nb_ptr)
727 * Get rid of all the old notebook tabs.
729 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
730 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
733 * Add to the specified byte view notebook tabs for hex dumps
734 * of all the data sources for the specified frame.
736 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
738 add_byte_tab(byte_nb_ptr, get_data_source_name(src), src->tvb, edt->tree,
743 * Initially select the first byte view.
745 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
750 static GtkWidget *savehex_dlg=NULL;
753 savehex_dlg_destroy_cb(void)
759 static void copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
761 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
763 gboolean end_of_line = TRUE; /* Initial state is end of line */
764 int byte_line_part_length;
769 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
770 hex_str = g_string_new("");
771 char_str= g_string_new("");
776 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
779 g_string_append_printf(hex_str," %02x",*data_p);
781 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
786 /* Look ahead to see if this is the end of the data */
787 byte_line_part_length = (++i) % byte_line_length;
789 /* End of data - need to fill in spaces in hex string and then do "end of line".
792 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
793 g_string_append(hex_str," "); /* Three spaces for each missing byte */
797 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
803 g_string_append(copy_buffer, hex_str->str);
805 /* Two spaces between hex and text */
806 g_string_append_c(copy_buffer, ' ');
807 g_string_append_c(copy_buffer, ' ');
808 g_string_append(copy_buffer, char_str->str);
810 /* Setup ready for next line */
811 g_string_assign(char_str,"");
812 g_string_assign(hex_str, "\n");
816 g_string_free(hex_str, TRUE);
817 g_string_free(char_str, TRUE);
821 int copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
826 if(isprint(*data_p)) {
828 } else if(*data_p==0x0a) {
829 to_append = '\n'; /* Copied from old copy_hex_cb function; not sure why this is needed - portablity?*/
831 return 1; /* Just ignore non-printable bytes */
833 g_string_append_c(copy_buffer,to_append);
838 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
840 g_string_append_printf(copy_buffer, "%02x", *data_p);
845 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
850 int bytes_consumed = 0;
853 const guint8* data_p;
855 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
857 bv = get_notebook_bv_ptr(byte_nb_ptr);
859 /* shouldn't happen */
860 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
864 data_p = get_byte_view_data_and_length(bv, &len);
865 g_assert(data_p != NULL);
867 flags = data_type & CD_FLAGSMASK;
868 data_type = data_type & CD_TYPEMASK;
870 if(flags & CD_FLAGS_SELECTEDONLY) {
873 /* Get the start and end of the highlighted bytes.
874 * XXX The keys appear to be REVERSED start <-> end throughout this file!
875 * Should this be fixed? There is one exception - packet_hex_reprint,
876 * so can't just change it round without functional change.
878 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
879 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
881 if(start >= 0 && end > start && (end - start <= (int)len)) {
889 /* This is too different from other text formats - handle separately */
890 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
893 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
894 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
897 /* Completely different logic to text copies - leave copy buffer alone */
898 copy_binary_to_clipboard(data_p,len);
901 /* Incrementally write to text buffer in various formats */
905 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
908 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
911 g_assert_not_reached();
915 g_assert(bytes_consumed>0);
916 data_p += bytes_consumed;
917 len -= bytes_consumed;
922 if(copy_buffer->len > 0) {
923 copy_to_clipboard(copy_buffer);
926 g_string_free(copy_buffer, TRUE);
929 /* save the current highlighted hex data */
931 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
936 const guint8 *data_p = NULL;
939 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
941 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
942 if (!file ||! *file) {
943 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
948 if (test_for_directory(file) == EISDIR) {
949 /* It's a directory - set the file selection box to display that
950 directory, and leave the selection box displayed. */
951 set_last_open_dir(file);
953 file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
954 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
955 return FALSE; /* do gtk_dialog_run again */
958 /* XXX: Must check if file name exists first */
960 bv = get_notebook_bv_ptr(byte_nb_ptr);
962 /* shouldn't happen */
963 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
968 * Retrieve the info we need
970 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
971 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
972 data_p = get_byte_view_data_and_length(bv, &len);
974 if (data_p == NULL || start == -1 || start > end) {
975 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
976 "No data selected to save!");
981 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
983 open_failure_alert_box(file, errno, TRUE);
987 if (ws_write(fd, data_p + start, end - start) < 0) {
988 write_failure_alert_box(file, errno);
993 if (ws_close(fd) < 0) {
994 write_failure_alert_box(file, errno);
999 /* Get rid of the dialog box */
1001 #if 0 /* being handled by caller (for now) */
1002 window_destroy(GTK_WIDGET(savehex_dlg));
1007 /* Launch the dialog box to put up the file selection box etc */
1008 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
1012 const guint8 *data_p = NULL;
1019 win32_export_raw_file(GDK_WINDOW_HWND(top_level->window));
1023 /* don't show up the dialog, if no data has to be saved */
1024 bv = get_notebook_bv_ptr(byte_nb_ptr);
1026 /* shouldn't happen */
1027 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
1030 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1031 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1032 data_p = get_byte_view_data_and_length(bv, &len);
1034 if (data_p == NULL || start == -1 || start > end) {
1035 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
1039 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
1040 /* if the window is already open, bring it to front */
1042 reactivate_window(savehex_dlg);
1047 * Build the dialog box we need.
1049 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
1050 #if GTK_CHECK_VERSION(2,8,0)
1051 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
1055 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
1056 end - start, plurality(end - start, "byte", "bytes"));
1057 dlg_lb = gtk_label_new(label);
1059 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
1060 gtk_widget_show(dlg_lb);
1062 g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
1065 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1066 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
1068 window_destroy(savehex_dlg);
1071 /* "Run" the GtkFileChooserDialog. */
1072 /* Upon exit: If "Accept" run the OK callback. */
1073 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
1074 /* If not accept (ie: cancel) destroy the window. */
1075 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
1076 /* return with a TRUE status so that the dialog window will be destroyed. */
1077 /* Trying to re-run the dialog after popping up an alert box will not work */
1078 /* since the user will not be able to dismiss the alert box. */
1079 /* The (somewhat unfriendly) effect: the user must re-invoke the */
1080 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
1082 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
1083 /* GtkFileChooserDialog. */
1084 while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
1085 if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
1086 break; /* we're done */
1089 window_destroy(savehex_dlg);
1094 /* Update the progress bar this many times when reading a file. */
1095 #define N_PROGBAR_UPDATES 100
1096 /* The minimum packet length required to check if a progres bar is needed or not */
1097 #define MIN_PACKET_LENGTH 1024
1100 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
1101 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
1102 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
1103 * presumably why there's a progress bar for it.)
1105 * Perhaps what's needed is a custom widget (either one that lets you stuff
1106 * text into it more quickly, or one that's a "virtual" widget so that the
1107 * text for a row is constructed, via a callback, when the row is to be
1108 * displayed). A custom widget might also let us treat the offset, hex
1109 * data, and ASCII data as three columns, so you can select purely in
1110 * the hex dump column.
1113 packet_hex_print_common(GtkWidget *bv, const guint8 *pd, int len, int bstart,
1114 int bend, int astart, int aend, int encoding)
1116 int i = 0, j, k = 0, b, cur;
1117 guchar line[MAX_LINES_LEN + 1];
1118 static guchar hexchars[16] = {
1119 '0', '1', '2', '3', '4', '5', '6', '7',
1120 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1121 static const guint8 bitmask[8] = {
1122 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
1124 unsigned int use_digits;
1125 gboolean reverse, newreverse;
1126 GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
1127 GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
1129 const char *revstyle;
1130 GtkTextMark *mark = NULL;
1132 progdlg_t *progbar = NULL;
1134 gboolean progbar_stop_flag;
1135 GTimeVal progbar_start_time;
1136 gchar progbar_status_str[100];
1137 int progbar_nextstep;
1138 int progbar_quantum;
1140 gtk_text_buffer_set_text(buf, "", 0);
1141 gtk_text_buffer_get_start_iter(buf, &iter);
1143 gtk_text_view_set_buffer( bv_text_view, NULL);
1146 * How many of the leading digits of the offset will we supply?
1147 * We always supply at least 4 digits, but if the maximum offset
1148 * won't fit in 4 digits, we use as many digits as will be needed.
1150 if (((len - 1) & 0xF0000000) != 0)
1151 use_digits = 8; /* need all 8 digits */
1152 else if (((len - 1) & 0x0F000000) != 0)
1153 use_digits = 7; /* need 7 digits */
1154 else if (((len - 1) & 0x00F00000) != 0)
1155 use_digits = 6; /* need 6 digits */
1156 else if (((len - 1) & 0x000F0000) != 0)
1157 use_digits = 5; /* need 5 digits */
1159 use_digits = 4; /* we'll supply 4 digits */
1161 /* Record the number of digits in this text view. */
1162 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1164 /* Update the progress bar when it gets to this value. */
1165 if (len > MIN_PACKET_LENGTH){
1166 progbar_nextstep = 0;
1168 /* If length =< MIN_PACKET_LENGTH
1169 * there is no need to calculate the progress
1171 progbar_nextstep = len+1;
1174 /* When we reach the value that triggers a progress bar update,
1175 bump that value by this amount. */
1176 progbar_quantum = len/N_PROGBAR_UPDATES;
1177 /* Progress so far. */
1180 progbar_stop_flag = FALSE;
1181 g_get_current_time(&progbar_start_time);
1185 /* Create the progress bar if necessary.
1186 We check on every iteration of the loop, so that it takes no
1187 longer than the standard time to create it (otherwise, for a
1188 large packet, we might take considerably longer than that standard
1189 time in order to get to the next progress bar step). */
1190 if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
1191 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1194 &progbar_start_time,
1197 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1198 when we update it, we have to run the GTK+ main loop to get it
1199 to repaint what's pending, and doing so may involve an "ioctl()"
1200 to see if there's any pending input from an X server, and doing
1201 that for every packet can be costly, especially on a big file. */
1202 if (i >= progbar_nextstep) {
1204 if (progbar != NULL) {
1205 /* let's not divide by zero. I should never be started
1206 * with count == 0, so let's assert that
1209 progbar_val = (gfloat) i / len;
1210 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1211 "%4u of %u bytes", i, len);
1212 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1215 progbar_nextstep += progbar_quantum;
1218 if (progbar_stop_flag) {
1219 /* Well, the user decided to abort the operation. Just stop,
1220 and arrange to return TRUE to our caller, so they know it
1221 was stopped explicitly. */
1225 /* Print the line number */
1229 c = (i >> (j*4)) & 0xF;
1230 line[cur++] = hexchars[c];
1235 /* Display with inverse video ? */
1236 if (prefs.gui_hex_dump_highlight_style)
1237 revstyle = "reverse";
1241 /* Do we start in reverse? */
1242 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1244 switch (recent.gui_bytes_view) {
1246 k = i + BYTES_PER_LINE;
1249 k = i + BITS_PER_LINE;
1252 g_assert_not_reached();
1255 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1259 /* Print the hex bit */
1262 switch (recent.gui_bytes_view) {
1264 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1265 line[cur++] = hexchars[pd[i] & 0x0f];
1268 for (b = 0; b < 8; b++) {
1269 line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
1273 g_assert_not_reached();
1276 switch (recent.gui_bytes_view) {
1278 line[cur++] = ' '; line[cur++] = ' ';
1281 for (b = 0; b < 8; b++) {
1286 g_assert_not_reached();
1290 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1291 /* Have we gone from reverse to plain? */
1292 if (reverse && (reverse != newreverse)) {
1293 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1297 /* Inter byte space if not at end of line */
1300 /* insert a space every BYTE_VIEW_SEP bytes */
1301 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1305 /* Have we gone from plain to reversed? */
1306 if (!reverse && (reverse != newreverse)) {
1307 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1309 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1312 reverse = newreverse;
1315 /* Print remaining part of line */
1316 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1321 /* Print some space at the end of the line */
1322 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1324 /* Print the ASCII bit */
1326 /* Do we start in reverse? */
1327 reverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1329 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1336 if (encoding == CHAR_ASCII) {
1339 else if (encoding == CHAR_EBCDIC) {
1340 c = EBCDIC_to_ASCII1(pd[i]);
1343 g_assert_not_reached();
1345 line[cur++] = isprint(c) ? c : '.';
1350 newreverse = (i >= bstart && i < bend) || (i >= astart && i < aend);
1351 /* Have we gone from reverse to plain? */
1352 if (reverse && (reverse != newreverse)) {
1353 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1359 /* insert a space every BYTE_VIEW_SEP bytes */
1360 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1364 /* Have we gone from plain to reversed? */
1365 if (!reverse && (reverse != newreverse)) {
1366 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1370 reverse = newreverse;
1372 /* Print remaining part of line */
1374 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1379 if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
1380 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1386 /* We're done printing the packets; destroy the progress bar if
1388 if (progbar != NULL)
1389 destroy_progress_dlg(progbar);
1391 /* scroll text into position */
1393 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1396 gtk_text_view_set_buffer( bv_text_view, buf);
1399 gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
1400 gtk_text_buffer_delete_mark(buf, mark);
1402 g_object_unref(buf);
1406 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
1407 field_info *finfo, guint len)
1409 /* do the initial printing and save the information needed */
1410 /* to redraw the display if preferences change. */
1412 int bstart = -1, bend = -1, blen = -1;
1413 int astart = -1, aend = -1, alen = -1;
1415 if (finfo != NULL) {
1416 bstart = finfo->start;
1417 blen = finfo->length;
1418 astart = finfo->appendix_start;
1419 alen = finfo->appendix_length;
1421 if (bstart >= 0 && blen >= 0) {
1422 bend = bstart + blen;
1424 if (astart >= 0 && alen >= 0) {
1425 aend = astart + alen;
1428 /* save the information needed to redraw the text */
1429 /* should we save the fd & finfo pointers instead ?? */
1430 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1431 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1432 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(aend));
1433 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(astart));
1434 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1435 GUINT_TO_POINTER((guint)fd->flags.encoding));
1437 packet_hex_print_common(bv, pd, len, bstart, bend, astart, aend, fd->flags.encoding);
1441 * Redraw the text using the saved information; usually called if
1442 * the preferences have changed.
1445 packet_hex_reprint(GtkWidget *bv)
1447 int start, end, encoding;
1452 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1453 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1454 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1455 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1456 data = get_byte_view_data_and_length(bv, &len);
1457 g_assert(data != NULL);
1458 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1460 packet_hex_print_common(bv, data, len, start, end, astart, aend, encoding);
1463 /* List of all protocol tree widgets, so we can globally set the selection
1464 mode and font of all of them. */
1465 static GList *ptree_widgets;
1467 /* Add a protocol tree widget to the list of protocol tree widgets. */
1468 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1471 remember_ptree_widget(GtkWidget *ptreew)
1473 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1475 /* Catch the "destroy" event on the widget, so that we remove it from
1476 the list when it's destroyed. */
1477 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1480 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1482 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1484 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1487 /* Set the selection mode of a given packet tree window. */
1489 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1491 GtkTreeSelection *selection;
1493 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1494 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1495 I think "browse" in Wireshark makes more sense than "SINGLE" in
1498 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1501 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1506 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1508 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1511 /* Set the selection mode of all packet tree windows. */
1513 set_ptree_sel_browse_all(gboolean val)
1515 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1519 set_ptree_font_cb(gpointer data, gpointer user_data)
1521 gtk_widget_modify_font((GtkWidget *)data,
1522 (PangoFontDescription *)user_data);
1526 set_ptree_font_all(PangoFontDescription *font)
1528 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1533 * Each expert_color_* level below should match the light gradient
1534 * colors in image/expert_indicators.svg.
1536 gboolean colors_ok = FALSE;
1537 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1538 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1539 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1540 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1541 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1542 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1544 gchar *expert_color_chat_str;
1545 gchar *expert_color_note_str;
1546 gchar *expert_color_warn_str;
1547 gchar *expert_color_error_str;
1548 gchar *expert_color_foreground_str;
1550 void proto_draw_colors_init(void)
1556 get_color(&expert_color_chat);
1557 get_color(&expert_color_note);
1558 get_color(&expert_color_warn);
1559 get_color(&expert_color_error);
1560 get_color(&expert_color_foreground);
1561 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1562 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1563 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1564 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1565 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1567 get_color(&hidden_proto_item);
1573 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1574 GtkCellRenderer *cell,
1575 GtkTreeModel *tree_model,
1581 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1584 proto_draw_colors_init();
1587 /* for the various possible attributes, see:
1588 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1590 * color definitions can be found at:
1591 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1592 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1595 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1596 * weight/style: doesn't take any effect
1599 /* for each field, we have to reset the renderer attributes */
1600 g_object_set (cell, "foreground-set", FALSE, NULL);
1602 g_object_set (cell, "background-set", FALSE, NULL);
1604 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1605 g_object_set (cell, "underline-set", FALSE, NULL);
1607 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1608 g_object_set (cell, "style-set", FALSE, NULL);*/
1610 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1611 g_object_set (cell, "weight-set", FALSE, NULL);*/
1613 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1614 /* we use "[...]" to mark generated items, no need to change things here */
1616 /* as some fonts don't support italic, don't use this */
1617 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1618 g_object_set (cell, "style-set", TRUE, NULL);
1620 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1621 g_object_set (cell, "weight-set", TRUE, NULL);*/
1624 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1625 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1626 g_object_set (cell, "foreground-set", TRUE, NULL);
1629 if (fi && fi->hfinfo) {
1630 if(fi->hfinfo->type == FT_PROTOCOL) {
1631 g_object_set (cell, "background", "gray90", NULL);
1632 g_object_set (cell, "background-set", TRUE, NULL);
1633 g_object_set (cell, "foreground", "black", NULL);
1634 g_object_set (cell, "foreground-set", TRUE, NULL);
1635 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1636 g_object_set (cell, "weight-set", TRUE, NULL);*/
1639 if((fi->hfinfo->type == FT_FRAMENUM) ||
1640 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1641 render_as_url(cell);
1645 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1646 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1648 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1649 g_object_set (cell, "background-set", TRUE, NULL);
1652 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1653 g_object_set (cell, "background-set", TRUE, NULL);
1656 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1657 g_object_set (cell, "background-set", TRUE, NULL);
1660 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1661 g_object_set (cell, "background-set", TRUE, NULL);
1664 g_assert_not_reached();
1666 g_object_set (cell, "foreground", "black", NULL);
1667 g_object_set (cell, "foreground-set", TRUE, NULL);
1672 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1674 GtkWidget *tv_scrollw, *tree_view;
1675 GtkTreeStore *store;
1676 GtkCellRenderer *renderer;
1677 GtkTreeViewColumn *column;
1681 tv_scrollw = scrolled_window_new(NULL, NULL);
1682 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1685 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1686 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1687 g_object_unref(G_OBJECT(store));
1688 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1689 renderer = gtk_cell_renderer_text_new();
1690 g_object_set (renderer, "ypad", 0, NULL);
1691 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1692 -1, "Name", renderer,
1694 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1696 gtk_tree_view_column_set_cell_data_func(column,
1702 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1703 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1704 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1705 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1706 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1707 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1708 gtk_widget_modify_font(tree_view, user_font_get_regular());
1709 remember_ptree_widget(tree_view);
1711 *tree_view_p = tree_view;
1716 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1719 for(i=0; i < num_tree_types; i++) {
1720 tree_is_expanded[i] = TRUE;
1722 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1725 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1728 for(i=0; i < num_tree_types; i++) {
1729 tree_is_expanded[i] = FALSE;
1731 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1735 struct proto_tree_draw_info {
1736 GtkTreeView *tree_view;
1741 main_proto_tree_draw(proto_tree *protocol_tree)
1743 proto_tree_draw(protocol_tree, tree_view);
1748 tree_view_follow_link(field_info *fi)
1752 if(fi->hfinfo->type == FT_FRAMENUM) {
1753 cf_goto_frame(&cfile, fi->value.value.uinteger);
1755 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1756 url = g_strndup(tvb_get_ptr(fi->ds_tvb, fi->start, fi->length), fi->length);
1757 browser_open_url(url);
1763 /* If the user selected a position in the tree view, try to find
1764 * the item in the GUI proto_tree that corresponds to that byte, and
1767 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1769 GtkTreeSelection *sel;
1772 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1773 (gint) (((GdkEventButton *)event)->x),
1774 (gint) (((GdkEventButton *)event)->y),
1775 &path, NULL, NULL, NULL))
1777 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1779 /* if that's a doubleclick, try to follow the link */
1780 if(event->type == GDK_2BUTTON_PRESS) {
1781 GtkTreeModel *model;
1785 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1786 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1787 tree_view_follow_link(fi);
1790 else if (((GdkEventButton *)event)->button != 1) {
1791 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1792 gtk_tree_selection_select_path(sel, path);
1800 /* fill the whole protocol tree with the string values */
1802 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1804 GtkTreeStore *store;
1805 struct proto_tree_draw_info info;
1807 info.tree_view = GTK_TREE_VIEW(tree_view);
1810 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1813 * Clear out any crud left over in the display of the protocol
1814 * tree, by removing all nodes from the tree.
1815 * This is how it's done in testgtk.c in GTK+.
1817 gtk_tree_store_clear(store);
1819 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
1823 /* fill a single protocol tree item with the string value */
1825 proto_tree_draw_node(proto_node *node, gpointer data)
1827 struct proto_tree_draw_info info;
1828 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
1830 field_info *fi = PNODE_FINFO(node);
1831 gchar label_str[ITEM_LABEL_LENGTH];
1833 gboolean is_leaf, is_expanded;
1834 GtkTreeStore *store;
1838 g_assert(fi && "dissection with an invisible proto tree?");
1840 if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
1843 /* was a free format label produced? */
1845 label_ptr = fi->rep->representation;
1847 else { /* no, make a generic label */
1848 label_ptr = label_str;
1849 proto_item_fill_label(fi, label_str);
1852 if (node->first_child != NULL) {
1854 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1855 if (tree_is_expanded[fi->tree_type]) {
1859 is_expanded = FALSE;
1864 is_expanded = FALSE;
1867 if (PROTO_ITEM_IS_GENERATED(node)) {
1868 if (PROTO_ITEM_IS_HIDDEN(node)) {
1869 label_ptr = g_strdup_printf("<[%s]>", label_ptr);
1871 label_ptr = g_strdup_printf("[%s]", label_ptr);
1873 } else if (PROTO_ITEM_IS_HIDDEN(node)) {
1874 label_ptr = g_strdup_printf("<%s>", label_ptr);
1877 info.tree_view = parent_info->tree_view;
1878 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
1879 gtk_tree_store_append(store, &iter, parent_info->iter);
1880 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
1882 if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
1888 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
1889 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
1891 gtk_tree_view_expand_to_path(info.tree_view, path);
1893 gtk_tree_view_collapse_row(info.tree_view, path);
1894 gtk_tree_path_free(path);
1899 * Clear the hex dump and protocol tree panes.
1902 clear_tree_and_hex_views(void)
1904 /* Clear the hex dump by getting rid of all the byte views. */
1905 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
1906 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
1908 /* Add a placeholder byte view so that there's at least something
1909 displayed in the byte view notebook. */
1910 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
1912 /* Clear the protocol tree by removing all nodes in the ctree.
1913 This is how it's done in testgtk.c in GTK+ */
1914 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));
1918 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
1920 if (recent.gui_bytes_view != view) {
1921 recent.gui_bytes_view = view;
1922 redraw_packet_bytes_all();