2 * Routines for GTK+ packet display (packet details and hex dump panes)
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
41 #include <gdk/gdkkeysyms.h>
42 #if GTK_CHECK_VERSION(3,0,0)
43 # include <gdk/gdkkeysyms-compat.h>
48 #include <epan/epan_dissect.h>
50 #include <epan/packet.h>
51 #include <epan/charsets.h>
52 #include <epan/prefs.h>
53 #include <epan/filesystem.h>
55 #include "../isprint.h"
57 #include "ui/alert_box.h"
58 #include "ui/last_open_dir.h"
59 #include "ui/progress_dlg.h"
60 #include "ui/recent.h"
61 #include "ui/simple_dialog.h"
62 #include "ui/ui_util.h"
64 #include <wsutil/file_util.h>
66 #include "ui/gtk/keys.h"
67 #include "ui/gtk/color_utils.h"
68 #include "ui/gtk/packet_win.h"
69 #include "ui/gtk/file_dlg.h"
70 #include "ui/gtk/gui_utils.h"
71 #include "ui/gtk/gtkglobals.h"
72 #include "ui/gtk/font_utils.h"
73 #include "ui/gtk/webbrowser.h"
74 #include "ui/gtk/main.h"
75 #include "ui/gtk/menus.h"
76 #include "ui/gtk/packet_panes.h"
77 #include "ui/gtk/proto_tree_model.h"
78 #include "ui/gtk/bytes_view.h"
81 #include <gdk/gdkwin32.h>
83 #include "ui/win32/file_dlg_win32.h"
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_TVBUFF_KEY "byte_view_tvbuff"
90 #define E_BYTE_VIEW_START_KEY "byte_view_start"
91 #define E_BYTE_VIEW_END_KEY "byte_view_end"
92 #define E_BYTE_VIEW_MASK_KEY "byte_view_mask"
93 #define E_BYTE_VIEW_MASKLE_KEY "byte_view_mask_le"
94 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
95 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
96 #define E_BYTE_VIEW_PROTO_START_KEY "byte_view_proto_start"
97 #define E_BYTE_VIEW_PROTO_END_KEY "byte_view_proto_end"
98 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
100 /* Get the current text window for the notebook. */
102 get_notebook_bv_ptr(GtkWidget *nb_ptr)
107 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
108 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
110 return gtk_bin_get_child(GTK_BIN(bv_page));
116 * Get the data and length for a byte view, given the byte view page.
117 * Return the pointer, or NULL on error, and set "*data_len" to the length.
120 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
122 tvbuff_t *byte_view_tvb;
123 const guint8 *data_ptr;
125 byte_view_tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
126 if (byte_view_tvb == NULL)
129 if ((*data_len = tvb_length(byte_view_tvb))) {
130 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
137 * Set the current text window for the notebook to the window that
138 * refers to a particular tvbuff.
141 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
144 GtkWidget *bv_page, *bv;
148 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
150 bv = gtk_bin_get_child(GTK_BIN(bv_page));
151 bv_tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
154 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
160 /* Redraw a given byte view window. */
162 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
168 bv = get_notebook_bv_ptr(nb);
170 data = get_byte_view_data_and_length(bv, &len);
172 packet_hex_print(bv, data, fd, finfo, len);
176 /* Redraw all byte view windows. */
178 redraw_packet_bytes_all(void)
180 if (cfile.current_frame != NULL)
181 redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
183 redraw_packet_bytes_packet_wins();
185 /* XXX - this is a hack, to workaround a bug in GTK2.x!
186 when changing the font size, even refilling of the corresponding
187 gtk_text_buffer doesn't seem to trigger an update.
188 The only workaround is to freshly select the frame, which will remove any
189 existing notebook tabs and "restart" the whole byte view again. */
190 if (cfile.current_frame != NULL) {
191 cfile.current_row = -1;
192 cf_goto_frame(&cfile, cfile.current_frame->num);
196 /* Expand trees (and any subtrees they may have) whose ett_ shows them as
198 * Callers should block calls to expand_tree() to avoid useless recursion.
201 check_expand_trees(GtkTreeView *tree_view, GtkTreeModel *model, GtkTreePath *path,
202 GtkTreeIter *iter, gboolean scroll_it, gboolean expand_parent)
204 /* code inspired by gtk_tree_model_foreach_helper */
211 if (gtk_tree_model_iter_children(model, &child, iter)) {
212 gtk_tree_model_get(model, iter, 1, &fi, -1);
214 if (tree_expanded(fi->tree_type)) {
216 gtk_tree_view_expand_row(tree_view, path, FALSE);
219 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
221 /* try to expand children only when parent is expanded */
222 gtk_tree_path_down(path);
223 check_expand_trees(tree_view, model, path, &child, scroll_it, TRUE);
224 gtk_tree_path_up(path);
228 gtk_tree_path_next(path);
229 } while (gtk_tree_model_iter_next(model, iter));
233 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
234 GtkTreePath *path, gpointer user_data _U_)
239 model = gtk_tree_view_get_model(tree_view);
240 gtk_tree_model_get(model, iter, 1, &finfo, -1);
243 /* scroll the expanded item to reduce the need to do a manual scroll down
244 * and provide faster navigation of deeper trees */
246 if(prefs.gui_auto_scroll_on_expand)
247 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
250 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
251 * are thus presumably leaf nodes and cannot be expanded.
253 if (finfo->tree_type != -1)
254 tree_expanded_set(finfo->tree_type, TRUE);
256 if (finfo->tree_type != -1 && path) {
257 /* Expand any subtrees that the user had left open */
258 g_signal_handlers_block_by_func(tree_view, expand_tree, NULL);
259 check_expand_trees(tree_view, model, path, iter, FALSE, FALSE);
260 g_signal_handlers_unblock_by_func(tree_view, expand_tree, NULL);
265 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
266 GtkTreePath *path _U_, gpointer user_data _U_)
271 model = gtk_tree_view_get_model(tree_view);
272 gtk_tree_model_get(model, iter, 1, &finfo, -1);
276 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
277 * are thus presumably leaf nodes and cannot be collapsed.
279 if (finfo->tree_type != -1)
280 tree_expanded_set(finfo->tree_type, FALSE);
283 struct field_lookup_info {
289 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
293 struct field_lookup_info *fli = (struct field_lookup_info *)data;
295 gtk_tree_model_get(model, iter, 1, &fi, -1);
304 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
307 struct field_lookup_info fli;
309 g_assert(finfo != NULL);
311 model = gtk_tree_view_get_model(tree_view);
313 gtk_tree_model_foreach(model, lookup_finfo, &fli);
315 return gtk_tree_model_get_path(model, &fli.iter);
318 /* If the user selected a certain byte in the byte view, try to find
319 * the item in the GUI proto_tree that corresponds to that byte, and:
321 * if we succeed, select it, and return TRUE;
322 * if we fail, return FALSE. */
324 byte_view_select(GtkWidget *widget, GdkEventButton *event)
327 GtkTreeView *tree_view;
331 tree = (proto_tree *)g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
334 * Somebody clicked on the dummy byte view; do nothing.
338 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
339 E_BYTE_VIEW_TREE_VIEW_PTR));
341 byte = bytes_view_byte_from_xy(BYTES_VIEW(widget), (gint) event->x, (gint) event->y);
347 /* Get the data source tvbuff */
348 tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
350 return highlight_field(tvb, byte, tree_view, tree);
353 /* This highlights the field in the proto tree that is at position byte */
355 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
358 GtkTreeModel *model = NULL;
359 GtkTreePath *first_path = NULL, *path = NULL;
361 field_info *finfo = NULL;
363 struct field_lookup_info fli;
365 if (cfile.search_in_progress && cfile.string && cfile.decode_data) {
366 /* The tree where the target string matched one of the labels was discarded in
367 match_protocol_tree() so we have to search again in the latest tree. (Uugh) */
368 if (cf_find_string_protocol_tree(&cfile, tree, &mdata)) {
372 /* Find the finfo that corresponds to our byte. */
373 finfo = proto_find_field_from_offset(tree, byte, tvb);
380 model = gtk_tree_view_get_model(tree_view);
382 gtk_tree_model_foreach(model, lookup_finfo, &fli);
384 /* Expand our field's row */
385 first_path = gtk_tree_model_get_path(model, &fli.iter);
386 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
387 expand_tree(tree_view, &fli.iter, NULL, NULL);
389 /* ... and its parents */
390 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
391 path = gtk_tree_model_get_path(model, &parent);
392 gtk_tree_view_expand_row(tree_view, path, FALSE);
393 expand_tree(tree_view, &parent, NULL, NULL);
395 gtk_tree_path_free(path);
398 /* Refresh the display so that the expanded trees are visible */
399 proto_tree_draw(tree, GTK_WIDGET(tree_view));
401 /* select our field's row */
402 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
405 /* If the last search was a string or hex search within "Packet data", the entire field might
406 not be highlighted. If the user just clicked on one of the bytes comprising that field, the
407 above call didn't trigger a 'gtk_tree_view_get_selection' event. Call redraw_packet_bytes()
408 to make the highlighting of the entire field visible. */
409 if (!cfile.search_in_progress) {
410 if (cfile.hex || (cfile.string && cfile.packet_data)) {
411 redraw_packet_bytes(byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
415 /* And position the window so the selection is visible.
416 * Position the selection in the middle of the viewable
418 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
420 gtk_tree_path_free(first_path);
425 /* Calls functions for different mouse-button presses. */
427 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
429 GdkEventButton *event_button = NULL;
431 if(widget == NULL || event == NULL || data == NULL) {
435 if(event->type == GDK_BUTTON_PRESS) {
436 event_button = (GdkEventButton *) event;
438 /* To qoute the "Gdk Event Structures" doc:
439 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
440 switch(event_button->button) {
443 return byte_view_select(widget, event_button);
445 return popup_menu_handler(widget, event, data);
459 byte_nb = gtk_notebook_new();
460 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
462 /* this will only have an effect, if no tabs are shown */
463 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
465 /* set the tabs scrollable, if they don't fit into the pane */
466 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
468 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
469 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
471 /* Add a placeholder byte view so that there's at least something
472 displayed in the byte view notebook. */
473 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
479 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
481 const guint8 *byte_data;
484 byte_data = get_byte_view_data_and_length(bv, &byte_len);
485 if (byte_data == NULL) {
486 /* This must be the dummy byte view if no packet is selected. */
489 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
493 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
494 proto_tree *tree, GtkWidget *tree_view)
496 GtkWidget *byte_view, *byte_scrollw, *label;
498 /* Byte view. Create a scrolled window for the text. */
499 byte_scrollw = scrolled_window_new(NULL, NULL);
500 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
502 /* Add scrolled pane to tabbed window */
503 label = gtk_label_new(name);
504 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
506 gtk_widget_show(byte_scrollw);
508 byte_view = bytes_view_new();
509 bytes_view_set_font(BYTES_VIEW(byte_view), user_font_get_regular());
511 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
512 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
514 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
515 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
516 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
518 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
519 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
521 gtk_widget_show(byte_view); /* triggers byte_view_realize_cb which calls packet_hex_print */
523 /* no tabs if this is the first page */
524 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
525 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
527 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
530 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
531 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
537 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
538 GtkWidget *byte_nb_ptr)
541 struct data_source *src;
544 * Get rid of all the old notebook tabs.
546 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
547 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
550 * Add to the specified byte view notebook tabs for hex dumps
551 * of all the data sources for the specified frame.
553 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
554 src = (struct data_source *)src_le->data;
555 add_byte_tab(byte_nb_ptr, get_data_source_name(src), get_data_source_tvb(src), edt->tree,
560 * Initially select the first byte view.
562 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
566 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
568 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
570 gboolean end_of_line = TRUE; /* Initial state is end of line */
571 int byte_line_part_length;
576 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
577 hex_str = g_string_new("");
578 char_str= g_string_new("");
583 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
586 g_string_append_printf(hex_str," %02x",*data_p);
588 g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
593 /* Look ahead to see if this is the end of the data */
594 byte_line_part_length = (++i) % byte_line_length;
596 /* End of data - need to fill in spaces in hex string and then do "end of line".
599 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
600 g_string_append(hex_str," "); /* Three spaces for each missing byte */
604 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
610 g_string_append(copy_buffer, hex_str->str);
612 /* Two spaces between hex and text */
613 g_string_append_c(copy_buffer, ' ');
614 g_string_append_c(copy_buffer, ' ');
615 g_string_append(copy_buffer, char_str->str);
617 /* Setup ready for next line */
618 g_string_assign(char_str,"");
619 g_string_assign(hex_str, "\n");
623 g_string_free(hex_str, TRUE);
624 g_string_free(char_str, TRUE);
628 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
633 /* Copy printable characters, newlines, and (horizontal) tabs. */
634 if(isprint(*data_p)) {
636 } else if(*data_p==0x0a) {
638 } else if(*data_p==0x09) {
641 return 1; /* Just ignore non-printable bytes */
643 g_string_append_c(copy_buffer,to_append);
648 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
650 g_string_append_printf(copy_buffer, "%02x", *data_p);
655 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
660 int bytes_consumed = 0;
663 const guint8* data_p;
665 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
667 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
669 /* shouldn't happen */
670 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window.");
674 data_p = get_byte_view_data_and_length(bv, &len);
675 g_assert(data_p != NULL);
677 flags = data_type & CD_FLAGSMASK;
678 data_type = (copy_data_type)(data_type & CD_TYPEMASK);
680 if(flags & CD_FLAGS_SELECTEDONLY) {
683 /* Get the start and end of the highlighted bytes. */
684 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
685 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
687 if(start >= 0 && end > start && (end - start <= (int)len)) {
695 /* This is too different from other text formats - handle separately */
696 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
699 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
700 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
703 /* Completely different logic to text copies - leave copy buffer alone */
704 copy_binary_to_clipboard(data_p,len);
707 /* Incrementally write to text buffer in various formats */
711 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
714 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
717 g_assert_not_reached();
721 g_assert(bytes_consumed>0);
722 data_p += bytes_consumed;
723 len -= bytes_consumed;
728 if(copy_buffer->len > 0) {
729 copy_to_clipboard(copy_buffer);
732 g_string_free(copy_buffer, TRUE);
735 /* save the current highlighted hex data */
737 savehex_save_clicked_cb(gchar *file, int start, int end, const guint8 *data_p)
741 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
743 open_failure_alert_box(file, errno, TRUE);
746 if (ws_write(fd, data_p + start, end - start) < 0) {
747 write_failure_alert_box(file, errno);
751 if (ws_close(fd) < 0) {
752 write_failure_alert_box(file, errno);
759 /* Launch the dialog box to put up the file selection box etc */
762 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
764 win32_export_raw_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
769 gtk_export_raw_file(int start, int end)
771 GtkWidget *savehex_dlg;
777 * Build the dialog box we need.
779 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", GTK_WINDOW(top_level), FILE_SELECTION_SAVE);
782 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
783 end - start, plurality(end - start, "byte", "bytes"));
784 dlg_lb = gtk_label_new(label);
786 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
787 gtk_widget_show(dlg_lb);
789 pathname = file_selection_run(savehex_dlg);
790 if (pathname == NULL) {
791 /* User cancelled or closed the dialog. */
795 /* We've crosed the Rubicon; get rid of the dialog box. */
796 window_destroy(savehex_dlg);
802 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
806 const guint8 *data_p = NULL;
810 /* don't show up the dialog, if no data has to be saved */
811 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
813 /* shouldn't happen */
814 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window.");
817 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
818 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
819 data_p = get_byte_view_data_and_length(bv, &len);
821 if (data_p == NULL || start == -1 || start > end) {
822 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save.");
827 * Loop until the user either selects a file or gives up.
830 pathname = gtk_export_raw_file(start, end);
831 if (pathname == NULL) {
835 if (savehex_save_clicked_cb(pathname, start, end, data_p)) {
840 /* Dump failed; let the user select another file or give up. */
847 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
848 int bend, guint32 bmask, int bmask_le,
849 int astart, int aend,
850 int pstart, int pend,
853 bytes_view_set_encoding(BYTES_VIEW(bv), encoding);
854 bytes_view_set_format(BYTES_VIEW(bv), recent.gui_bytes_view);
855 bytes_view_set_data(BYTES_VIEW(bv), pd, len);
857 bytes_view_set_highlight_style(BYTES_VIEW(bv), prefs.gui_hex_dump_highlight_style);
859 bytes_view_set_highlight(BYTES_VIEW(bv), bstart, bend, bmask, bmask_le);
860 bytes_view_set_highlight_extra(BYTES_VIEW(bv), BYTE_VIEW_HIGHLIGHT_APPENDIX, astart, aend);
861 bytes_view_set_highlight_extra(BYTES_VIEW(bv), BYTE_VIEW_HIGHLIGHT_PROTOCOL, pstart, pend);
863 if (bstart != -1 && bend != -1)
864 bytes_view_scroll_to_byte(BYTES_VIEW(bv), bstart);
865 bytes_view_refresh(BYTES_VIEW(bv));
869 get_top_finfo(proto_node *node, field_info *finfo)
876 if (PNODE_FINFO(node) == finfo) {
879 while (node && node->parent) {
884 fi = PNODE_FINFO(node);
885 if (fi && fi->ds_tvb == finfo->ds_tvb)
892 for (child = node->first_child; child; child = child->next) {
893 top = get_top_finfo(child, finfo);
902 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
903 field_info *finfo, guint len)
905 /* do the initial printing and save the information needed */
906 /* to redraw the display if preferences change. */
908 int bstart = -1, bend = -1, blen = -1;
909 guint32 bmask = 0x00; int bmask_le = 0;
910 int astart = -1, aend = -1, alen = -1;
911 int pstart = -1, pend = -1, plen = -1;
914 proto_tree *tree = (proto_tree *)g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TREE_PTR);
915 field_info *top_finfo;
917 if (cfile.search_in_progress && (cfile.hex || (cfile.string && cfile.packet_data))) {
918 /* In the hex view, only highlight the target bytes or string. The entire
919 field can then be displayed by clicking on any of the bytes in the field. */
921 char *p = cfile.sfilter;
925 if (g_ascii_isxdigit(*p++))
928 blen = (blen + 1) / 2;
930 blen = (int)strlen(cfile.sfilter);
932 bstart = cfile.search_pos - (blen-1);
935 blen = finfo->length;
936 bstart = finfo->start;
939 /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
940 if (finfo->hfinfo) bmask = finfo->hfinfo->bitmask;
941 astart = finfo->appendix_start;
942 alen = finfo->appendix_length;
944 top_finfo = get_top_finfo(tree, finfo);
945 /* it's possible to have top_finfo == finfo, no problem right now */
947 pstart = top_finfo->start;
948 plen = top_finfo->length;
951 if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
953 else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
955 else { /* unknown endianess - disable mask
956 bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
962 int bito = FI_GET_BITS_OFFSET(finfo);
963 int bitc = FI_GET_BITS_SIZE(finfo);
964 int bitt = bito + bitc;
966 /* construct mask using bito & bitc */
967 /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
968 if (bitt > 0 && bitt < 32) {
970 bmask = ((1 << bitc) - 1) << ((8-bitt) & 7);
971 bmask_le = 0; /* ? */
976 if (pstart >= 0 && plen > 0 && (guint)pstart < len)
977 pend = pstart + plen;
979 if (bstart >= 0 && blen > 0 && (guint)bstart < len)
980 bend = bstart + blen;
982 if (astart >= 0 && alen > 0 && (guint)astart < len)
983 aend = astart + alen;
985 if (bend == -1 && aend != -1) {
992 /* don't exceed the end of available data */
993 if (aend != -1 && (guint)aend > len) aend = len;
994 if (bend != -1 && (guint)bend > len) bend = len;
995 if (pend != -1 && (guint)pend > len) pend = len;
997 /* save the information needed to redraw the text */
998 /* should we save the fd & finfo pointers instead ?? */
999 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1000 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1001 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1002 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1003 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1004 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1005 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY, GINT_TO_POINTER(pstart));
1006 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY, GINT_TO_POINTER(pend));
1007 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1008 GUINT_TO_POINTER((guint)fd->flags.encoding));
1010 /* stig: it should be done only for bitview... */
1011 if (recent.gui_bytes_view != BYTES_BITS)
1013 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, pstart, pend, fd->flags.encoding);
1017 packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd, int offset, int bitoffset, guint len)
1019 /* do the initial printing and save the information needed */
1020 /* to redraw the display if preferences change. */
1022 int bstart = offset, bend = (bstart != -1) ? offset+1 : -1;
1023 guint32 bmask=0; int bmask_le = 0;
1024 int astart = -1, aend = -1;
1025 int pstart = -1, pend = -1;
1027 switch (recent.gui_bytes_view) {
1029 bmask = (bitoffset == 0) ? 0xf0 : (bitoffset == 4) ? 0x0f : 0xff;
1033 bmask = (1 << (7-bitoffset));
1037 g_assert_not_reached();
1041 /* save the information needed to redraw the text */
1042 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1043 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1044 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1045 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1046 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1047 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1048 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1049 GUINT_TO_POINTER((guint)fd->flags.encoding));
1050 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY, GINT_TO_POINTER(pstart));
1051 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY, GINT_TO_POINTER(pend));
1053 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, pstart, pend, fd->flags.encoding);
1057 * Redraw the text using the saved information; usually called if
1058 * the preferences have changed.
1061 packet_hex_reprint(GtkWidget *bv)
1063 int start, end, mask, mask_le, encoding;
1069 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1070 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1071 mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1072 mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1073 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1074 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1075 pstart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY));
1076 pend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY));
1077 data = get_byte_view_data_and_length(bv, &len);
1078 g_assert(data != NULL);
1079 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1081 /* stig: it should be done only for bitview... */
1082 if (recent.gui_bytes_view != BYTES_BITS)
1084 packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, pstart, pend, encoding);
1087 /* List of all protocol tree widgets, so we can globally set the selection
1088 mode and font of all of them. */
1089 static GList *ptree_widgets;
1091 /* Add a protocol tree widget to the list of protocol tree widgets. */
1092 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1095 remember_ptree_widget(GtkWidget *ptreew)
1097 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1099 /* Catch the "destroy" event on the widget, so that we remove it from
1100 the list when it's destroyed. */
1101 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1104 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1106 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1108 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1112 set_ptree_font_cb(gpointer data, gpointer user_data)
1114 #if GTK_CHECK_VERSION(3,0,0)
1115 gtk_widget_override_font((GtkWidget *)data,
1116 (PangoFontDescription *)user_data);
1118 gtk_widget_modify_font((GtkWidget *)data,
1119 (PangoFontDescription *)user_data);
1124 set_ptree_font_all(PangoFontDescription *font)
1126 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1131 * Each expert_color_* level below should match the light gradient
1132 * colors in image/expert_indicators.svg.
1134 static gboolean colors_ok = FALSE;
1136 GdkColor expert_color_comment = { 0, 0xb7b7, 0xf7f7, 0x7474 }; /* Green */
1137 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1138 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1139 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1140 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1141 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1142 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1144 gchar *expert_color_comment_str;
1145 gchar *expert_color_chat_str;
1146 gchar *expert_color_note_str;
1147 gchar *expert_color_warn_str;
1148 gchar *expert_color_error_str;
1149 gchar *expert_color_foreground_str;
1151 void proto_draw_colors_init(void)
1157 /* Allocating collor isn't necessary? */
1158 get_color(&expert_color_chat);
1159 get_color(&expert_color_note);
1160 get_color(&expert_color_warn);
1161 get_color(&expert_color_error);
1162 get_color(&expert_color_foreground);
1164 expert_color_comment_str = gdk_color_to_string(&expert_color_comment);
1165 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1166 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1167 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1168 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1169 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1172 get_color(&hidden_proto_item);
1179 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1180 GtkTreeModel *tree_model, GtkTreeIter *iter,
1185 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1188 proto_draw_colors_init();
1191 /* for the various possible attributes, see:
1192 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1194 * color definitions can be found at:
1195 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1196 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1199 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1200 * weight/style: doesn't take any effect
1203 /* for each field, we have to reset the renderer attributes */
1204 g_object_set (cell, "foreground-set", FALSE, NULL);
1206 g_object_set (cell, "background-set", FALSE, NULL);
1208 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1209 g_object_set (cell, "underline-set", FALSE, NULL);
1211 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1212 g_object_set (cell, "style-set", FALSE, NULL);*/
1214 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1215 g_object_set (cell, "weight-set", FALSE, NULL);*/
1217 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1218 /* we use "[...]" to mark generated items, no need to change things here */
1220 /* as some fonts don't support italic, don't use this */
1221 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1222 g_object_set (cell, "style-set", TRUE, NULL);
1224 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1225 g_object_set (cell, "weight-set", TRUE, NULL);*/
1228 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1229 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1230 g_object_set (cell, "foreground-set", TRUE, NULL);
1233 if (fi && fi->hfinfo) {
1234 if(fi->hfinfo->type == FT_PROTOCOL) {
1235 g_object_set (cell, "background", "gray90", NULL);
1236 g_object_set (cell, "background-set", TRUE, NULL);
1237 g_object_set (cell, "foreground", "black", NULL);
1238 g_object_set (cell, "foreground-set", TRUE, NULL);
1239 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1240 g_object_set (cell, "weight-set", TRUE, NULL);*/
1243 if((fi->hfinfo->type == FT_FRAMENUM) ||
1244 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1245 render_as_url(cell);
1249 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1250 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1252 g_object_set (cell, "background-gdk", &expert_color_comment, NULL);
1253 g_object_set (cell, "background-set", TRUE, NULL);
1256 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1257 g_object_set (cell, "background-set", TRUE, NULL);
1260 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1261 g_object_set (cell, "background-set", TRUE, NULL);
1264 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1265 g_object_set (cell, "background-set", TRUE, NULL);
1268 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1269 g_object_set (cell, "background-set", TRUE, NULL);
1272 g_assert_not_reached();
1274 g_object_set (cell, "foreground", "black", NULL);
1275 g_object_set (cell, "foreground-set", TRUE, NULL);
1280 proto_tree_view_new(GtkWidget **tree_view_p)
1282 GtkWidget *tv_scrollw, *tree_view;
1283 ProtoTreeModel *store;
1284 GtkCellRenderer *renderer;
1285 GtkTreeViewColumn *column;
1289 tv_scrollw = scrolled_window_new(NULL, NULL);
1290 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1293 store = proto_tree_model_new(NULL, prefs.display_hidden_proto_items);
1294 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1295 g_object_unref(G_OBJECT(store));
1296 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1297 renderer = gtk_cell_renderer_text_new();
1298 g_object_set (renderer, "ypad", 0, NULL);
1299 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1300 -1, "Name", renderer,
1302 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1304 gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1307 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1308 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1309 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1310 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1311 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1312 #if GTK_CHECK_VERSION(3,0,0)
1313 gtk_widget_override_font(tree_view, user_font_get_regular());
1315 gtk_widget_modify_font(tree_view, user_font_get_regular());
1317 remember_ptree_widget(tree_view);
1319 *tree_view_p = tree_view;
1325 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1329 for(i=0; i < num_tree_types; i++)
1330 tree_expanded_set(i, TRUE);
1332 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1336 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1340 for(i=0; i < num_tree_types; i++)
1341 tree_expanded_set(i, FALSE);
1343 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1347 tree_view_follow_link(field_info *fi)
1351 if(fi->hfinfo->type == FT_FRAMENUM) {
1352 cf_goto_frame(&cfile, fi->value.value.uinteger);
1354 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1355 url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL);
1357 browser_open_url(url);
1364 /* If the user selected a position in the tree view, try to find
1365 * the item in the GUI proto_tree that corresponds to that byte, and
1368 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1370 GtkTreeSelection *sel;
1373 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1374 (gint) (((GdkEventButton *)event)->x),
1375 (gint) (((GdkEventButton *)event)->y),
1376 &path, NULL, NULL, NULL))
1378 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1380 /* if that's a doubleclick, try to follow the link */
1381 if(event->type == GDK_2BUTTON_PRESS) {
1382 GtkTreeModel *model;
1386 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1387 if (event->state & GDK_SHIFT_MASK) {
1388 new_packet_window(NULL, TRUE, FALSE);
1391 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1392 tree_view_follow_link(fi);
1396 else if (((GdkEventButton *)event)->button != 1) {
1397 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1398 gtk_tree_selection_select_path(sel, path);
1407 proto_tree_draw_resolve(proto_tree *protocol_tree, GtkWidget *tree_view, const e_addr_resolve *resolv)
1409 ProtoTreeModel *model;
1413 model = proto_tree_model_new(protocol_tree, prefs.display_hidden_proto_items);
1415 proto_tree_model_force_resolv(PROTO_TREE_MODEL(model), resolv);
1416 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(model));
1418 g_signal_handlers_block_by_func(tree_view, expand_tree, NULL);
1420 /* modified version of gtk_tree_model_foreach */
1421 path = gtk_tree_path_new_first();
1422 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
1423 check_expand_trees(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(model),
1424 path, &iter, prefs.gui_auto_scroll_on_expand, TRUE);
1425 gtk_tree_path_free(path);
1427 g_signal_handlers_unblock_by_func(tree_view, expand_tree, NULL);
1429 g_object_unref(G_OBJECT(model));
1432 /* fill the whole protocol tree with the string values */
1434 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1436 proto_tree_draw_resolve(protocol_tree, tree_view, NULL);
1440 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
1442 if (recent.gui_bytes_view != view) {
1443 recent.gui_bytes_view = view;
1444 redraw_packet_bytes_all();