2 * Routines for GTK+ packet display (packet details and hex dump panes)
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * Jeff Foster, 2001/03/12, added support for displaying named
9 * data sources as tabbed hex windows
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 #include <gdk/gdkkeysyms.h>
38 #if GTK_CHECK_VERSION(3,0,0)
39 # include <gdk/gdkkeysyms-compat.h>
44 #include <epan/epan_dissect.h>
46 #include <epan/packet.h>
47 #include <epan/charsets.h>
48 #include <epan/prefs.h>
49 #include <wsutil/filesystem.h>
51 #include "ui/alert_box.h"
52 #include "ui/last_open_dir.h"
53 #include "ui/progress_dlg.h"
54 #include "ui/recent.h"
55 #include "ui/simple_dialog.h"
56 #include "ui/ui_util.h"
58 #include <wsutil/file_util.h>
60 #include "ui/gtk/keys.h"
61 #include "ui/gtk/color_utils.h"
62 #include "ui/gtk/packet_win.h"
63 #include "ui/gtk/file_dlg.h"
64 #include "ui/gtk/gui_utils.h"
65 #include "ui/gtk/gtkglobals.h"
66 #include "ui/gtk/font_utils.h"
67 #include "ui/gtk/webbrowser.h"
68 #include "ui/gtk/main.h"
69 #include "ui/gtk/menus.h"
70 #include "ui/gtk/packet_panes.h"
71 #include "ui/gtk/proto_tree_model.h"
72 #include "ui/gtk/bytes_view.h"
75 #include <gdk/gdkwin32.h>
77 #include "ui/win32/file_dlg_win32.h"
81 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
82 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
83 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
84 #define E_BYTE_VIEW_START_KEY "byte_view_start"
85 #define E_BYTE_VIEW_END_KEY "byte_view_end"
86 #define E_BYTE_VIEW_MASK_LO_KEY "byte_view_mask_lo"
87 #define E_BYTE_VIEW_MASK_HI_KEY "byte_view_mask_hi"
88 #define E_BYTE_VIEW_MASKLE_KEY "byte_view_mask_le"
89 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
90 #define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
91 #define E_BYTE_VIEW_PROTO_START_KEY "byte_view_proto_start"
92 #define E_BYTE_VIEW_PROTO_END_KEY "byte_view_proto_end"
93 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
95 /* Get the current text window for the notebook. */
97 get_notebook_bv_ptr(GtkWidget *nb_ptr)
102 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
103 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
105 return gtk_bin_get_child(GTK_BIN(bv_page));
111 * Get the data and length for a byte view, given the byte view page.
112 * Return the pointer, or NULL on error, and set "*data_len" to the length.
115 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
117 tvbuff_t *byte_view_tvb;
118 const guint8 *data_ptr;
120 byte_view_tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
121 if (byte_view_tvb == NULL)
124 if ((*data_len = tvb_length(byte_view_tvb))) {
125 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
132 * Set the current text window for the notebook to the window that
133 * refers to a particular tvbuff.
136 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
139 GtkWidget *bv_page, *bv;
143 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
145 bv = gtk_bin_get_child(GTK_BIN(bv_page));
146 bv_tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
149 gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
155 /* Redraw a given byte view window. */
157 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
163 bv = get_notebook_bv_ptr(nb);
165 data = get_byte_view_data_and_length(bv, &len);
167 packet_hex_print(bv, data, fd, finfo, len);
171 /* Redraw all byte view windows. */
173 redraw_packet_bytes_all(void)
175 if (cfile.current_frame != NULL)
176 redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
178 redraw_packet_bytes_packet_wins();
180 /* XXX - this is a hack, to workaround a bug in GTK2.x!
181 when changing the font size, even refilling of the corresponding
182 gtk_text_buffer doesn't seem to trigger an update.
183 The only workaround is to freshly select the frame, which will remove any
184 existing notebook tabs and "restart" the whole byte view again. */
185 if (cfile.current_frame != NULL) {
186 cfile.current_row = -1;
187 cf_goto_frame(&cfile, cfile.current_frame->num);
191 /* Expand trees (and any subtrees they may have) whose ett_ shows them as
193 * Callers should block calls to expand_tree() to avoid useless recursion.
196 check_expand_trees(GtkTreeView *tree_view, GtkTreeModel *model, GtkTreePath *path,
197 GtkTreeIter *iter, gboolean scroll_it, gboolean expand_parent)
199 /* code inspired by gtk_tree_model_foreach_helper */
206 if (gtk_tree_model_iter_children(model, &child, iter)) {
207 gtk_tree_model_get(model, iter, 1, &fi, -1);
209 if (tree_expanded(fi->tree_type)) {
211 gtk_tree_view_expand_row(tree_view, path, FALSE);
214 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
216 /* try to expand children only when parent is expanded */
217 gtk_tree_path_down(path);
218 check_expand_trees(tree_view, model, path, &child, scroll_it, TRUE);
219 gtk_tree_path_up(path);
223 gtk_tree_path_next(path);
224 } while (gtk_tree_model_iter_next(model, iter));
228 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
229 GtkTreePath *path, gpointer user_data _U_)
234 model = gtk_tree_view_get_model(tree_view);
235 gtk_tree_model_get(model, iter, 1, &finfo, -1);
238 /* scroll the expanded item to reduce the need to do a manual scroll down
239 * and provide faster navigation of deeper trees */
241 if(prefs.gui_auto_scroll_on_expand)
242 gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
245 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
246 * are thus presumably leaf nodes and cannot be expanded.
248 if (finfo->tree_type != -1)
249 tree_expanded_set(finfo->tree_type, TRUE);
251 if (finfo->tree_type != -1 && path) {
252 /* Expand any subtrees that the user had left open */
253 g_signal_handlers_block_by_func(tree_view, expand_tree, NULL);
254 check_expand_trees(tree_view, model, path, iter, FALSE, FALSE);
255 g_signal_handlers_unblock_by_func(tree_view, expand_tree, NULL);
260 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
261 GtkTreePath *path _U_, gpointer user_data _U_)
266 model = gtk_tree_view_get_model(tree_view);
267 gtk_tree_model_get(model, iter, 1, &finfo, -1);
271 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
272 * are thus presumably leaf nodes and cannot be collapsed.
274 if (finfo->tree_type != -1)
275 tree_expanded_set(finfo->tree_type, FALSE);
278 struct field_lookup_info {
284 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
288 struct field_lookup_info *fli = (struct field_lookup_info *)data;
290 gtk_tree_model_get(model, iter, 1, &fi, -1);
299 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
302 struct field_lookup_info fli;
304 g_assert(finfo != NULL);
306 model = gtk_tree_view_get_model(tree_view);
308 gtk_tree_model_foreach(model, lookup_finfo, &fli);
310 return gtk_tree_model_get_path(model, &fli.iter);
313 /* If the user selected a certain byte in the byte view, try to find
314 * the item in the GUI proto_tree that corresponds to that byte, and:
316 * if we succeed, select it, and return TRUE;
317 * if we fail, return FALSE. */
319 byte_view_select(GtkWidget *widget, GdkEventButton *event)
322 GtkTreeView *tree_view;
326 tree = (proto_tree *)g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
329 * Somebody clicked on the dummy byte view; do nothing.
333 tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
334 E_BYTE_VIEW_TREE_VIEW_PTR));
336 byte = bytes_view_byte_from_xy(BYTES_VIEW(widget), (gint) event->x, (gint) event->y);
342 /* Get the data source tvbuff */
343 tvb = (tvbuff_t *)g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
345 return highlight_field(tvb, byte, tree_view, tree);
348 /* This highlights the field in the proto tree that is at position byte */
350 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
353 GtkTreeModel *model = NULL;
354 GtkTreePath *first_path = NULL, *path = NULL;
356 field_info *finfo = NULL;
358 struct field_lookup_info fli;
360 if (cfile.search_in_progress && cfile.string && cfile.decode_data) {
361 /* The tree where the target string matched one of the labels was discarded in
362 match_protocol_tree() so we have to search again in the latest tree. (Uugh) */
363 if (cf_find_string_protocol_tree(&cfile, tree, &mdata)) {
367 /* Find the finfo that corresponds to our byte. */
368 finfo = proto_find_field_from_offset(tree, byte, tvb);
375 model = gtk_tree_view_get_model(tree_view);
377 gtk_tree_model_foreach(model, lookup_finfo, &fli);
379 /* Expand our field's row */
380 first_path = gtk_tree_model_get_path(model, &fli.iter);
381 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
382 expand_tree(tree_view, &fli.iter, NULL, NULL);
384 /* ... and its parents */
385 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
386 path = gtk_tree_model_get_path(model, &parent);
387 gtk_tree_view_expand_row(tree_view, path, FALSE);
388 expand_tree(tree_view, &parent, NULL, NULL);
390 gtk_tree_path_free(path);
393 /* select our field's row */
394 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
397 /* If the last search was a string or hex search within "Packet data", the entire field might
398 not be highlighted. If the user just clicked on one of the bytes comprising that field, the
399 above call didn't trigger a 'gtk_tree_view_get_selection' event. Call redraw_packet_bytes()
400 to make the highlighting of the entire field visible. */
401 if (!cfile.search_in_progress) {
402 if (cfile.hex || (cfile.string && cfile.packet_data)) {
403 redraw_packet_bytes(byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
407 /* And position the window so the selection is visible.
408 * Position the selection in the middle of the viewable
410 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
412 gtk_tree_path_free(first_path);
417 /* Calls functions for different mouse-button presses. */
419 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
421 GdkEventButton *event_button = NULL;
423 if(widget == NULL || event == NULL || data == NULL) {
427 if(event->type == GDK_BUTTON_PRESS) {
428 event_button = (GdkEventButton *) event;
430 /* To qoute the "Gdk Event Structures" doc:
431 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
432 switch(event_button->button) {
435 return byte_view_select(widget, event_button);
437 return popup_menu_handler(widget, event, data);
451 byte_nb = gtk_notebook_new();
452 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
454 /* this will only have an effect, if no tabs are shown */
455 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
457 /* set the tabs scrollable, if they don't fit into the pane */
458 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
460 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
461 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
463 /* Add a placeholder byte view so that there's at least something
464 displayed in the byte view notebook. */
465 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
471 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
473 const guint8 *byte_data;
476 byte_data = get_byte_view_data_and_length(bv, &byte_len);
477 if (byte_data == NULL) {
478 /* This must be the dummy byte view if no packet is selected. */
481 packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
485 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
486 proto_tree *tree, GtkWidget *tree_view)
488 GtkWidget *byte_view, *byte_scrollw, *label;
490 /* Byte view. Create a scrolled window for the text. */
491 byte_scrollw = scrolled_window_new(NULL, NULL);
492 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
494 /* Add scrolled pane to tabbed window */
495 label = gtk_label_new(name);
496 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
498 gtk_widget_show(byte_scrollw);
500 byte_view = bytes_view_new();
501 bytes_view_set_font(BYTES_VIEW(byte_view), user_font_get_regular());
503 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
504 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
506 g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
507 g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
508 g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
510 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
511 g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
513 gtk_widget_show(byte_view); /* triggers byte_view_realize_cb which calls packet_hex_print */
515 /* no tabs if this is the first page */
516 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
517 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
519 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
522 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
523 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
529 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
530 GtkWidget *byte_nb_ptr)
533 struct data_source *src;
536 * Get rid of all the old notebook tabs.
538 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
539 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
542 * Add to the specified byte view notebook tabs for hex dumps
543 * of all the data sources for the specified frame.
545 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
546 src = (struct data_source *)src_le->data;
547 add_byte_tab(byte_nb_ptr, get_data_source_name(src), get_data_source_tvb(src), edt->tree,
552 * Initially select the first byte view.
554 gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
558 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
560 const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
562 gboolean end_of_line = TRUE; /* Initial state is end of line */
563 int byte_line_part_length;
568 /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
569 hex_str = g_string_new("");
570 char_str= g_string_new("");
575 g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
578 g_string_append_printf(hex_str," %02x",*data_p);
580 g_string_append_printf(char_str,"%c",g_ascii_isprint(*data_p) ? *data_p : '.');
585 /* Look ahead to see if this is the end of the data */
586 byte_line_part_length = (++i) % byte_line_length;
588 /* End of data - need to fill in spaces in hex string and then do "end of line".
591 for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
592 g_string_append(hex_str," "); /* Three spaces for each missing byte */
596 end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
602 g_string_append(copy_buffer, hex_str->str);
604 /* Two spaces between hex and text */
605 g_string_append_c(copy_buffer, ' ');
606 g_string_append_c(copy_buffer, ' ');
607 g_string_append(copy_buffer, char_str->str);
609 /* Setup ready for next line */
610 g_string_assign(char_str,"");
611 g_string_assign(hex_str, "\n");
615 g_string_free(hex_str, TRUE);
616 g_string_free(char_str, TRUE);
620 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
625 /* Copy printable characters, newlines, and (horizontal) tabs. */
626 if(g_ascii_isprint(*data_p)) {
628 } else if(*data_p==0x0a) {
630 } else if(*data_p==0x09) {
633 return 1; /* Just ignore non-printable bytes */
635 g_string_append_c(copy_buffer,to_append);
640 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
642 g_string_append_printf(copy_buffer, "%02x", *data_p);
647 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
652 int bytes_consumed = 0;
655 const guint8* data_p;
657 GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
659 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
661 /* shouldn't happen */
662 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window.");
666 data_p = get_byte_view_data_and_length(bv, &len);
667 g_assert(data_p != NULL);
669 flags = data_type & CD_FLAGSMASK;
670 data_type = (copy_data_type)(data_type & CD_TYPEMASK);
672 if(flags & CD_FLAGS_SELECTEDONLY) {
675 /* Get the start and end of the highlighted bytes. */
676 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
677 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
679 if(start >= 0 && end > start && (end - start <= (int)len)) {
687 /* This is too different from other text formats - handle separately */
688 copy_hex_all_info(copy_buffer, data_p, len, TRUE);
691 /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
692 copy_hex_all_info(copy_buffer, data_p, len, FALSE);
695 /* Completely different logic to text copies - leave copy buffer alone */
696 copy_binary_to_clipboard(data_p,len);
699 /* Incrementally write to text buffer in various formats */
703 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
706 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
709 g_assert_not_reached();
713 g_assert(bytes_consumed>0);
714 data_p += bytes_consumed;
715 len -= bytes_consumed;
720 if(copy_buffer->len > 0) {
721 copy_to_clipboard(copy_buffer);
724 g_string_free(copy_buffer, TRUE);
727 /* save the current highlighted hex data */
729 savehex_save_clicked_cb(gchar *file, int start, int end, const guint8 *data_p)
733 fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
735 open_failure_alert_box(file, errno, TRUE);
738 if (ws_write(fd, data_p + start, end - start) < 0) {
739 write_failure_alert_box(file, errno);
743 if (ws_close(fd) < 0) {
744 write_failure_alert_box(file, errno);
751 /* Launch the dialog box to put up the file selection box etc */
754 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
756 win32_export_raw_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), &cfile);
761 gtk_export_raw_file(int start, int end)
763 GtkWidget *savehex_dlg;
769 * Build the dialog box we need.
771 savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", GTK_WINDOW(top_level), FILE_SELECTION_SAVE);
774 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
775 end - start, plurality(end - start, "byte", "bytes"));
776 dlg_lb = gtk_label_new(label);
778 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
779 gtk_widget_show(dlg_lb);
781 pathname = file_selection_run(savehex_dlg);
782 if (pathname == NULL) {
783 /* User cancelled or closed the dialog. */
787 /* We've crosed the Rubicon; get rid of the dialog box. */
788 window_destroy(savehex_dlg);
794 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
798 const guint8 *data_p = NULL;
802 /* don't show up the dialog, if no data has to be saved */
803 bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
805 /* shouldn't happen */
806 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window.");
809 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
810 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
811 data_p = get_byte_view_data_and_length(bv, &len);
813 if (data_p == NULL || start == -1 || start > end) {
814 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save.");
819 * Loop until the user either selects a file or gives up.
822 pathname = gtk_export_raw_file(start, end);
823 if (pathname == NULL) {
827 if (savehex_save_clicked_cb(pathname, start, end, data_p)) {
832 /* Dump failed; let the user select another file or give up. */
839 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
840 int bend, guint64 bmask, int bmask_le,
841 int astart, int aend,
842 int pstart, int pend,
845 bytes_view_set_encoding(BYTES_VIEW(bv), encoding);
846 bytes_view_set_format(BYTES_VIEW(bv), recent.gui_bytes_view);
847 bytes_view_set_data(BYTES_VIEW(bv), pd, len);
849 bytes_view_set_highlight_style(BYTES_VIEW(bv), prefs.gui_hex_dump_highlight_style);
851 bytes_view_set_highlight(BYTES_VIEW(bv), bstart, bend, bmask, bmask_le);
852 bytes_view_set_highlight_extra(BYTES_VIEW(bv), BYTE_VIEW_HIGHLIGHT_APPENDIX, astart, aend);
853 bytes_view_set_highlight_extra(BYTES_VIEW(bv), BYTE_VIEW_HIGHLIGHT_PROTOCOL, pstart, pend);
855 if (bstart != -1 && bend != -1)
856 bytes_view_scroll_to_byte(BYTES_VIEW(bv), bstart);
857 bytes_view_refresh(BYTES_VIEW(bv));
861 get_top_finfo(proto_node *node, field_info *finfo)
868 if (PNODE_FINFO(node) == finfo) {
871 while (node && node->parent) {
876 fi = PNODE_FINFO(node);
877 if (fi && fi->ds_tvb == finfo->ds_tvb)
884 for (child = node->first_child; child; child = child->next) {
885 top = get_top_finfo(child, finfo);
894 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
895 field_info *finfo, guint len)
897 /* do the initial printing and save the information needed */
898 /* to redraw the display if preferences change. */
900 int bstart = -1, bend = -1, blen = -1;
901 guint64 bmask = 0x00; int bmask_le = 0;
902 int astart = -1, aend = -1, alen = -1;
903 int pstart = -1, pend = -1, plen = -1;
906 proto_tree *tree = (proto_tree *)g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TREE_PTR);
907 field_info *top_finfo;
909 if (cfile.search_in_progress && (cfile.hex || (cfile.string && cfile.packet_data))) {
910 /* In the hex view, only highlight the target bytes or string. The entire
911 field can then be displayed by clicking on any of the bytes in the field. */
913 char *p = cfile.sfilter;
917 if (g_ascii_isxdigit(*p++))
920 blen = (blen + 1) / 2;
922 blen = (int)strlen(cfile.sfilter);
924 bstart = cfile.search_pos - (blen-1);
927 blen = finfo->length;
928 bstart = finfo->start;
931 /* bmask = finfo->hfinfo->bitmask << hfinfo_bitshift(finfo->hfinfo); */ /* (value & mask) >> shift */
932 if (finfo->hfinfo) bmask = finfo->hfinfo->bitmask;
933 astart = finfo->appendix_start;
934 alen = finfo->appendix_length;
936 top_finfo = get_top_finfo(tree, finfo);
937 /* it's possible to have top_finfo == finfo, no problem right now */
939 pstart = top_finfo->start;
940 plen = top_finfo->length;
943 if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
945 else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
947 else { /* unknown endianess - disable mask
948 bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
954 int bito = FI_GET_BITS_OFFSET(finfo);
955 int bitc = FI_GET_BITS_SIZE(finfo);
956 int bitt = bito + bitc;
958 /* construct mask using bito & bitc */
959 /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
960 if (bitt > 0 && bitt < 32) {
962 bmask = ((G_GUINT64_CONSTANT(1) << bitc) - 1) << ((8-bitt) & 7);
963 bmask_le = 0; /* ? */
968 if (pstart >= 0 && plen > 0 && (guint)pstart < len)
969 pend = pstart + plen;
971 if (bstart >= 0 && blen > 0 && (guint)bstart < len)
972 bend = bstart + blen;
974 if (astart >= 0 && alen > 0 && (guint)astart < len)
975 aend = astart + alen;
977 if (bend == -1 && aend != -1) {
984 /* don't exceed the end of available data */
985 if (aend != -1 && (guint)aend > len) aend = len;
986 if (bend != -1 && (guint)bend > len) bend = len;
987 if (pend != -1 && (guint)pend > len) pend = len;
989 /* save the information needed to redraw the text */
990 /* should we save the fd & finfo pointers instead ?? */
991 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
992 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
993 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_LO_KEY, GINT_TO_POINTER((guint32) bmask));
994 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_HI_KEY, GINT_TO_POINTER(bmask >> 32));
995 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
996 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
997 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
998 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY, GINT_TO_POINTER(pstart));
999 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY, GINT_TO_POINTER(pend));
1000 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1001 GUINT_TO_POINTER((guint)fd->flags.encoding));
1003 /* stig: it should be done only for bitview... */
1004 if (recent.gui_bytes_view != BYTES_BITS)
1006 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, pstart, pend, fd->flags.encoding);
1010 packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd, int offset, int bitoffset, guint len)
1012 /* do the initial printing and save the information needed */
1013 /* to redraw the display if preferences change. */
1015 int bstart = offset, bend = (bstart != -1) ? offset+1 : -1;
1016 guint64 bmask=0; int bmask_le = 0;
1017 int astart = -1, aend = -1;
1018 int pstart = -1, pend = -1;
1020 switch (recent.gui_bytes_view) {
1022 bmask = (bitoffset == 0) ? 0xf0 : (bitoffset == 4) ? 0x0f : 0xff;
1026 bmask = (G_GUINT64_CONSTANT(1) << (7-bitoffset));
1030 g_assert_not_reached();
1034 /* save the information needed to redraw the text */
1035 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1036 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1037 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_LO_KEY, GINT_TO_POINTER((guint32) bmask));
1038 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_HI_KEY, GINT_TO_POINTER(bmask >> 32));
1039 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1040 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1041 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1042 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1043 GUINT_TO_POINTER((guint)fd->flags.encoding));
1044 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY, GINT_TO_POINTER(pstart));
1045 g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY, GINT_TO_POINTER(pend));
1047 packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, pstart, pend, fd->flags.encoding);
1051 * Redraw the text using the saved information; usually called if
1052 * the preferences have changed.
1055 packet_hex_reprint(GtkWidget *bv)
1057 int start, end, mask_le, encoding;
1064 start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1065 end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1066 mask = (guint64) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_HI_KEY)) << 32 |
1067 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_LO_KEY));
1068 mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1069 astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1070 aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1071 pstart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_START_KEY));
1072 pend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_PROTO_END_KEY));
1073 data = get_byte_view_data_and_length(bv, &len);
1074 g_assert(data != NULL);
1075 encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1077 /* stig: it should be done only for bitview... */
1078 if (recent.gui_bytes_view != BYTES_BITS)
1080 packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, pstart, pend, encoding);
1083 /* List of all protocol tree widgets, so we can globally set the selection
1084 mode and font of all of them. */
1085 static GList *ptree_widgets;
1087 /* Add a protocol tree widget to the list of protocol tree widgets. */
1088 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1091 remember_ptree_widget(GtkWidget *ptreew)
1093 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1095 /* Catch the "destroy" event on the widget, so that we remove it from
1096 the list when it's destroyed. */
1097 g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1100 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1102 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1104 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1108 set_ptree_font_cb(gpointer data, gpointer user_data)
1110 #if GTK_CHECK_VERSION(3,0,0)
1111 gtk_widget_override_font((GtkWidget *)data,
1112 (PangoFontDescription *)user_data);
1114 gtk_widget_modify_font((GtkWidget *)data,
1115 (PangoFontDescription *)user_data);
1120 set_ptree_font_all(PangoFontDescription *font)
1122 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1127 * Each expert_color_* level below should match the light gradient
1128 * colors in image/expert_indicators.svg.
1130 static gboolean colors_ok = FALSE;
1132 GdkColor expert_color_comment = { 0, 0xb7b7, 0xf7f7, 0x7474 }; /* Green */
1133 GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
1134 GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
1135 GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
1136 GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
1137 GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
1138 GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
1140 gchar *expert_color_comment_str;
1141 gchar *expert_color_chat_str;
1142 gchar *expert_color_note_str;
1143 gchar *expert_color_warn_str;
1144 gchar *expert_color_error_str;
1145 gchar *expert_color_foreground_str;
1147 void proto_draw_colors_init(void)
1153 /* Allocating collor isn't necessary? */
1154 get_color(&expert_color_chat);
1155 get_color(&expert_color_note);
1156 get_color(&expert_color_warn);
1157 get_color(&expert_color_error);
1158 get_color(&expert_color_foreground);
1160 expert_color_comment_str = gdk_color_to_string(&expert_color_comment);
1161 expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1162 expert_color_note_str = gdk_color_to_string(&expert_color_note);
1163 expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1164 expert_color_error_str = gdk_color_to_string(&expert_color_error);
1165 expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1168 get_color(&hidden_proto_item);
1175 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1176 GtkTreeModel *tree_model, GtkTreeIter *iter,
1181 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1184 proto_draw_colors_init();
1187 /* for the various possible attributes, see:
1188 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1190 * color definitions can be found at:
1191 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1192 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1195 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1196 * weight/style: doesn't take any effect
1199 /* for each field, we have to reset the renderer attributes */
1200 g_object_set (cell, "foreground-set", FALSE, NULL);
1202 g_object_set (cell, "background-set", FALSE, NULL);
1204 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1205 g_object_set (cell, "underline-set", FALSE, NULL);
1207 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1208 g_object_set (cell, "style-set", FALSE, NULL);*/
1210 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1211 g_object_set (cell, "weight-set", FALSE, NULL);*/
1213 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1214 /* we use "[...]" to mark generated items, no need to change things here */
1216 /* as some fonts don't support italic, don't use this */
1217 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1218 g_object_set (cell, "style-set", TRUE, NULL);
1220 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1221 g_object_set (cell, "weight-set", TRUE, NULL);*/
1224 if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1225 g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1226 g_object_set (cell, "foreground-set", TRUE, NULL);
1229 if (fi && fi->hfinfo) {
1230 if(fi->hfinfo->type == FT_PROTOCOL) {
1231 g_object_set (cell, "background", "gray90", NULL);
1232 g_object_set (cell, "background-set", TRUE, NULL);
1233 g_object_set (cell, "foreground", "black", NULL);
1234 g_object_set (cell, "foreground-set", TRUE, NULL);
1235 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1236 g_object_set (cell, "weight-set", TRUE, NULL);*/
1239 if((fi->hfinfo->type == FT_FRAMENUM) ||
1240 (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1241 render_as_url(cell);
1245 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1246 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1248 g_object_set (cell, "background-gdk", &expert_color_comment, NULL);
1249 g_object_set (cell, "background-set", TRUE, NULL);
1252 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1253 g_object_set (cell, "background-set", TRUE, NULL);
1256 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1257 g_object_set (cell, "background-set", TRUE, NULL);
1260 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1261 g_object_set (cell, "background-set", TRUE, NULL);
1264 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1265 g_object_set (cell, "background-set", TRUE, NULL);
1268 g_assert_not_reached();
1270 g_object_set (cell, "foreground", "black", NULL);
1271 g_object_set (cell, "foreground-set", TRUE, NULL);
1276 proto_tree_view_new(GtkWidget **tree_view_p)
1278 GtkWidget *tv_scrollw, *tree_view;
1279 ProtoTreeModel *store;
1280 GtkCellRenderer *renderer;
1281 GtkTreeViewColumn *column;
1285 tv_scrollw = scrolled_window_new(NULL, NULL);
1286 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1289 store = proto_tree_model_new(NULL, prefs.display_hidden_proto_items);
1290 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1291 g_object_unref(G_OBJECT(store));
1292 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1293 renderer = gtk_cell_renderer_text_new();
1294 g_object_set (renderer, "ypad", 0, NULL);
1295 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1296 -1, "Name", renderer,
1298 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1300 gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1303 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1304 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1305 g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1306 g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1307 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1308 #if GTK_CHECK_VERSION(3,0,0)
1309 gtk_widget_override_font(tree_view, user_font_get_regular());
1311 gtk_widget_modify_font(tree_view, user_font_get_regular());
1313 remember_ptree_widget(tree_view);
1315 *tree_view_p = tree_view;
1321 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1325 for(i=0; i < num_tree_types; i++)
1326 tree_expanded_set(i, TRUE);
1328 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1332 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1336 for(i=0; i < num_tree_types; i++)
1337 tree_expanded_set(i, FALSE);
1339 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1343 tree_view_follow_link(field_info *fi)
1347 if(fi->hfinfo->type == FT_FRAMENUM) {
1348 cf_goto_frame(&cfile, fi->value.value.uinteger);
1350 if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1351 url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL);
1353 browser_open_url(url);
1360 /* If the user selected a position in the tree view, try to find
1361 * the item in the GUI proto_tree that corresponds to that byte, and
1364 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1366 GtkTreeSelection *sel;
1369 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1370 (gint) (((GdkEventButton *)event)->x),
1371 (gint) (((GdkEventButton *)event)->y),
1372 &path, NULL, NULL, NULL))
1374 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1376 /* if that's a doubleclick, try to follow the link */
1377 if(event->type == GDK_2BUTTON_PRESS) {
1378 GtkTreeModel *model;
1382 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1383 if (event->state & GDK_SHIFT_MASK) {
1384 new_packet_window(NULL, TRUE, FALSE);
1387 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1388 tree_view_follow_link(fi);
1392 else if (((GdkEventButton *)event)->button != 1) {
1393 /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1394 gtk_tree_selection_select_path(sel, path);
1403 proto_tree_draw_resolve(proto_tree *protocol_tree, GtkWidget *tree_view, const e_addr_resolve *resolv)
1405 ProtoTreeModel *model;
1409 model = proto_tree_model_new(protocol_tree, prefs.display_hidden_proto_items);
1411 proto_tree_model_force_resolv(PROTO_TREE_MODEL(model), resolv);
1412 gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(model));
1414 g_signal_handlers_block_by_func(tree_view, expand_tree, NULL);
1416 /* modified version of gtk_tree_model_foreach */
1417 path = gtk_tree_path_new_first();
1418 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
1419 check_expand_trees(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(model),
1420 path, &iter, prefs.gui_auto_scroll_on_expand, TRUE);
1421 gtk_tree_path_free(path);
1423 g_signal_handlers_unblock_by_func(tree_view, expand_tree, NULL);
1425 g_object_unref(G_OBJECT(model));
1428 /* fill the whole protocol tree with the string values */
1430 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1432 proto_tree_draw_resolve(protocol_tree, tree_view, NULL);
1436 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
1438 if (recent.gui_bytes_view != view) {
1439 recent.gui_bytes_view = view;
1440 redraw_packet_bytes_all();