2 * Routines for GTK+ packet display
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
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.
35 #include <io.h> /* open/close on win32 */
47 #include <gdk/gdkkeysyms.h>
51 #include <epan/epan_dissect.h>
56 #include <epan/packet.h>
57 #include <epan/charsets.h>
61 #include <epan/prefs.h>
64 #include "proto_draw.h"
65 #include "packet_win.h"
66 #include "dlg_utils.h"
67 #include "gui_utils.h"
68 #include "gtkglobals.h"
69 #include "compat_macros.h"
70 #include "alert_box.h"
71 #include "simple_dialog.h"
72 #include "progress_dlg.h"
73 #include "font_utils.h"
75 #include "../ui_util.h"
77 /* Win32 needs the O_BINARY flag for open() */
82 #define BYTE_VIEW_WIDTH 16
83 #define BYTE_VIEW_SEP 8
85 #define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
86 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
87 #define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
88 #define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
89 #define E_BYTE_VIEW_START_KEY "byte_view_start"
90 #define E_BYTE_VIEW_END_KEY "byte_view_end"
91 #define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
94 #if GTK_MAJOR_VERSION < 2
95 GtkStyle *item_style = NULL;
98 /* gtk_tree_view_expand_to_path doesn't exist in gtk+ v2.0 so we must include it
99 * when building with this version (taken from gtk+ v2.2.4) */
100 #if GTK_MAJOR_VERSION >= 2 && GTK_MINOR_VERSION == 0
102 * gtk_tree_view_expand_to_path:
103 * @tree_view: A #GtkTreeView.
104 * @path: path to a row.
106 * Expands the row at @path. This will also expand all parent rows of
107 * @path as necessary.
112 gtk_tree_view_expand_to_path (GtkTreeView *tree_view,
119 g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
120 g_return_if_fail (path != NULL);
122 depth = gtk_tree_path_get_depth (path);
123 indices = gtk_tree_path_get_indices (path);
125 tmp = gtk_tree_path_new ();
126 g_return_if_fail (tmp != NULL);
128 for (i = 0; i < depth; i++)
130 gtk_tree_path_append_index (tmp, indices[i]);
131 gtk_tree_view_expand_row (tree_view, tmp, FALSE);
134 gtk_tree_path_free (tmp);
139 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
140 proto_tree *tree, GtkWidget *tree_view);
143 proto_tree_draw_node(proto_node *node, gpointer data);
145 /* Get the current text window for the notebook. */
147 get_notebook_bv_ptr(GtkWidget *nb_ptr)
152 num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
153 bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
155 return GTK_BIN(bv_page)->child;
161 * Get the data and length for a byte view, given the byte view page.
162 * Return the pointer, or NULL on error, and set "*data_len" to the length.
165 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
167 tvbuff_t *byte_view_tvb;
168 const guint8 *data_ptr;
170 byte_view_tvb = OBJECT_GET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY);
171 if (byte_view_tvb == NULL)
174 data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
175 *data_len = tvb_length(byte_view_tvb);
180 * Set the current text window for the notebook to the window that
181 * refers to a particular tvbuff.
184 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
187 GtkWidget *bv_page, *bv;
191 (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
193 bv = GTK_BIN(bv_page)->child;
194 bv_tvb = OBJECT_GET_DATA(bv, E_BYTE_VIEW_TVBUFF_KEY);
197 gtk_notebook_set_page(GTK_NOTEBOOK(nb_ptr), num);
203 /* Redraw a given byte view window. */
205 redraw_hex_dump(GtkWidget *nb, frame_data *fd, field_info *finfo)
211 bv = get_notebook_bv_ptr(nb);
213 data = get_byte_view_data_and_length(bv, &len);
215 #if GTK_MAJOR_VERSION < 2
216 packet_hex_print(GTK_TEXT(bv), data, fd, finfo, len);
218 packet_hex_print(GTK_TEXT_VIEW(bv), data, fd, finfo, len);
223 /* Redraw all byte view windows. */
225 redraw_hex_dump_all(void)
227 if (cfile.current_frame != NULL)
228 redraw_hex_dump( byte_nb_ptr, cfile.current_frame, cfile.finfo_selected);
230 redraw_hex_dump_packet_wins();
232 #if GTK_MAJOR_VERSION >= 2
233 /* XXX - this is a hack, to workaround a bug in GTK2.x!
234 when changing the font size, even refilling of the corresponding
235 gtk_text_buffer doesn't seem to trigger an update.
236 The only workaround is to freshly select the frame, which will remove any
237 existing notebook tabs and "restart" the whole byte view again. */
238 if (cfile.current_frame != NULL)
239 cf_goto_frame(&cfile, cfile.current_frame->num);
243 #if GTK_MAJOR_VERSION < 2
245 expand_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
248 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
249 GtkTreePath *path _U_, gpointer user_data _U_)
253 #if GTK_MAJOR_VERSION >= 2
256 model = gtk_tree_view_get_model(tree_view);
257 gtk_tree_model_get(model, iter, 1, &finfo, -1);
259 finfo = gtk_ctree_node_get_row_data( ctree, node);
264 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
265 * are thus presumably leaf nodes and cannot be expanded.
267 if (finfo->tree_type != -1) {
268 g_assert(finfo->tree_type >= 0 &&
269 finfo->tree_type < num_tree_types);
270 tree_is_expanded[finfo->tree_type] = TRUE;
274 #if GTK_MAJOR_VERSION < 2
276 collapse_tree(GtkCTree *ctree, GtkCTreeNode *node, gpointer user_data _U_)
279 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
280 GtkTreePath *path _U_, gpointer user_data _U_)
284 #if GTK_MAJOR_VERSION >= 2
287 model = gtk_tree_view_get_model(tree_view);
288 gtk_tree_model_get(model, iter, 1, &finfo, -1);
290 finfo = gtk_ctree_node_get_row_data( ctree, node);
295 * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
296 * are thus presumably leaf nodes and cannot be collapsed.
298 if (finfo->tree_type != -1) {
299 g_assert(finfo->tree_type >= 0 &&
300 finfo->tree_type < num_tree_types);
301 tree_is_expanded[finfo->tree_type] = FALSE;
305 #if GTK_MAJOR_VERSION < 2
307 toggle_tree(GtkCTree *ctree, GdkEventKey *event, gpointer user_data _U_)
309 if (event->keyval != GDK_Return)
311 gtk_ctree_toggle_expansion(ctree, GTK_CTREE_NODE(ctree->clist.selection->data));
315 #define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
316 #define BYTES_PER_LINE 16 /* max byte values in a line */
317 #define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
318 /* max number of characters hex dump takes -
319 2 digits plus trailing blank
320 plus separator between first and
322 #define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
323 /* number of characters those bytes take;
324 3 characters per byte of hex dump,
325 2 blanks separating hex from ASCII,
326 1 character per byte of ASCII dump */
327 #define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
328 /* number of characters per line;
329 offset, 2 blanks separating offset
330 from data dump, data dump */
332 /* Which byte the offset is referring to. Associates
333 * whitespace with the preceding digits. */
335 byte_num(int offset, int start_point)
337 return (offset - start_point) / 3;
340 #if GTK_MAJOR_VERSION >= 2
341 struct field_lookup_info {
347 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
351 struct field_lookup_info *fli = (struct field_lookup_info *)data;
353 gtk_tree_model_get(model, iter, 1, &fi, -1);
361 GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo) {
363 struct field_lookup_info fli;
365 g_assert(finfo != NULL);
367 model = gtk_tree_view_get_model(tree_view);
369 gtk_tree_model_foreach(model, lookup_finfo, &fli);
371 return gtk_tree_model_get_path(model, &fli.iter);
376 /* If the user selected a certain byte in the byte view, try to find
377 * the item in the GUI proto_tree that corresponds to that byte, and:
379 * if we succeed, select it, and return TRUE;
380 * if we fail, return FALSE. */
382 byte_view_select(GtkWidget *widget, GdkEventButton *event)
385 #if GTK_MAJOR_VERSION < 2
387 GtkCTreeNode *node, *parent;
388 GtkText *bv = GTK_TEXT(widget);
390 GtkTreeView *tree_view;
392 GtkTreePath *first_path, *path;
394 GtkTextView *bv = GTK_TEXT_VIEW(widget);
397 struct field_lookup_info fli;
414 * Get the number of digits of offset being displayed, and
415 * compute the columns of various parts of the display.
417 ndigits = GPOINTER_TO_UINT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY));
420 * The column of the first hex digit in the first half.
421 * That starts after "ndigits" digits of offset and two
424 digits_start_1 = ndigits + 2;
427 * The column of the last hex digit in the first half.
428 * There are BYTES_PER_LINE/2 bytes displayed in the first
429 * half; there are 2 characters per byte, plus a separating
430 * blank after all but the last byte's characters.
432 * Then subtract 1 to get the last column of the first half
433 * rather than the first column after the first half.
435 digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
436 (BYTES_PER_LINE/2 - 1) - 1;
439 * The column of the first hex digit in the second half.
440 * Add back the 1 to get the first column after the first
441 * half, and then add 2 for the 2 separating blanks between
444 digits_start_2 = digits_end_1 + 3;
447 * The column of the last hex digit in the second half.
448 * Add the same value we used to get "digits_end_1" from
451 digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
452 (BYTES_PER_LINE/2 - 1) - 1;
455 * The column of the first "text dump" character in the first half.
456 * Add back the 1 to get the first column after the second
457 * half's hex dump, and then add 3 for the 3 separating blanks
458 * between the hex and text dummp.
460 text_start_1 = digits_end_2 + 4;
463 * The column of the last "text dump" character in the first half.
464 * There are BYTES_PER_LINE/2 bytes displayed in the first
465 * half; there is 1 character per byte.
467 * Then subtract 1 to get the last column of the first half
468 * rather than the first column after the first half.
470 text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
473 * The column of the first "text dump" character in the second half.
474 * Add back the 1 to get the first column after the first half,
475 * and then add 1 for the separating blank between the halves.
477 text_start_2 = text_end_1 + 2;
480 * The column of the last "text dump" character in second half.
481 * Add the same value we used to get "text_end_1" from
484 text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
486 tree = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_PTR);
489 * Somebody clicked on the dummy byte view; do nothing.
493 #if GTK_MAJOR_VERSION < 2
494 ctree = GTK_CTREE(OBJECT_GET_DATA(widget, E_BYTE_VIEW_TREE_VIEW_PTR));
496 tree_view = GTK_TREE_VIEW(OBJECT_GET_DATA(widget,
497 E_BYTE_VIEW_TREE_VIEW_PTR));
500 #if GTK_MAJOR_VERSION < 2
501 /* Given the mouse (x,y) and the current GtkText (h,v)
502 * adjustments, and the size of the font, figure out
503 * which text column/row the user selected. This could be off
504 * if the bold version of the font is bigger than the
505 * regular version of the font. */
506 column = (int) ((bv->hadj->value + event->x) / user_font_get_regular_width());
507 row = (int) ((bv->vadj->value + event->y) / user_font_get_regular_height());
509 /* get the row/column selected */
510 gtk_text_view_window_to_buffer_coords(bv,
511 gtk_text_view_get_window_type(bv, event->window),
512 (gint) event->x, (gint) event->y, &x, &y);
513 gtk_text_view_get_iter_at_location(bv, &iter, x, y);
514 row = gtk_text_iter_get_line(&iter);
515 column = gtk_text_iter_get_line_offset(&iter);
518 /* Given the column and row, determine which byte offset
519 * the user clicked on. */
520 if (column >= digits_start_1 && column <= digits_end_1) {
521 byte = byte_num(column, digits_start_1);
526 else if (column >= digits_start_2 && column <= digits_end_2) {
527 byte = byte_num(column, digits_start_2);
533 else if (column >= text_start_1 && column <= text_end_1) {
534 byte = column - text_start_1;
536 else if (column >= text_start_2 && column <= text_end_2) {
537 byte = 8 + column - text_start_2;
540 /* The user didn't select a hex digit or
541 * text-dump character. */
545 /* Add the number of bytes from the previous rows. */
548 /* Get the data source tvbuff */
549 tvb = OBJECT_GET_DATA(widget, E_BYTE_VIEW_TVBUFF_KEY);
551 /* Find the finfo that corresponds to our byte. */
552 finfo = proto_find_field_from_offset(tree, byte, tvb);
558 #if GTK_MAJOR_VERSION < 2
559 node = gtk_ctree_find_by_row_data(ctree, NULL, finfo);
562 /* Expand and select our field's row */
563 gtk_ctree_expand(ctree, node);
564 gtk_ctree_select(ctree, node);
565 expand_tree(ctree, node, NULL);
567 /* ... and its parents */
568 parent = GTK_CTREE_ROW(node)->parent;
570 gtk_ctree_expand(ctree, parent);
571 expand_tree(ctree, parent, NULL);
572 parent = GTK_CTREE_ROW(parent)->parent;
575 /* And position the window so the selection is visible.
576 * Position the selection in the middle of the viewable
578 gtk_ctree_node_moveto(ctree, node, 0, .5, 0);
582 model = gtk_tree_view_get_model(tree_view);
584 gtk_tree_model_foreach(model, lookup_finfo, &fli);
586 /* Expand our field's row */
587 first_path = gtk_tree_model_get_path(model, &fli.iter);
588 gtk_tree_view_expand_row(tree_view, first_path, FALSE);
589 expand_tree(tree_view, &fli.iter, NULL, NULL);
591 /* ... and its parents */
592 while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
593 path = gtk_tree_model_get_path(model, &parent);
594 gtk_tree_view_expand_row(tree_view, path, FALSE);
595 expand_tree(tree_view, &parent, NULL, NULL);
597 gtk_tree_path_free(path);
600 /* select our field's row */
601 gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
604 /* And position the window so the selection is visible.
605 * Position the selection in the middle of the viewable
607 gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5, 0.0);
609 gtk_tree_path_free(first_path);
615 /* Calls functions for different mouse-button presses. */
617 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
619 GdkEventButton *event_button = NULL;
621 if(widget == NULL || event == NULL || data == NULL) {
625 if(event->type == GDK_BUTTON_PRESS) {
626 event_button = (GdkEventButton *) event;
628 /* To qoute the "Gdk Event Structures" doc:
629 * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
630 switch(event_button->button) {
633 return byte_view_select(widget, event_button);
635 return popup_menu_handler(widget, event, data);
649 byte_nb = gtk_notebook_new();
650 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
652 /* this will only have an effect, if no tabs are shown */
653 gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
655 /* set the tabs scrollable, if they don't fit into the pane */
656 gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
658 /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
659 gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
661 /* Add a placeholder byte view so that there's at least something
662 displayed in the byte view notebook. */
663 add_byte_tab(byte_nb, "", NULL, NULL, NULL);
669 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
671 const guint8 *byte_data;
674 byte_data = get_byte_view_data_and_length(bv, &byte_len);
675 if (byte_data == NULL) {
676 /* This must be the dummy byte view if no packet is selected. */
679 #if GTK_MAJOR_VERSION < 2
680 packet_hex_print(GTK_TEXT(bv), byte_data, cfile.current_frame, NULL,
683 packet_hex_print(GTK_TEXT_VIEW(bv), byte_data, cfile.current_frame, NULL,
689 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
690 proto_tree *tree, GtkWidget *tree_view)
692 GtkWidget *byte_view, *byte_scrollw, *label;
693 #if GTK_MAJOR_VERSION >= 2
698 /* Byte view. Create a scrolled window for the text. */
699 byte_scrollw = scrolled_window_new(NULL, NULL);
700 #if GTK_MAJOR_VERSION < 2
701 /* The horizontal scrollbar of the scroll-window doesn't seem
702 * to affect the GtkText widget at all, even when line wrapping
703 * is turned off in the GtkText widget and there is indeed more
704 * horizontal data. */
705 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(byte_scrollw),
706 /* Horizontal */GTK_POLICY_NEVER,
707 /* Vertical*/ GTK_POLICY_ALWAYS);
709 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
712 /* Add scrolled pane to tabbed window */
713 label = gtk_label_new(name);
714 gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
716 gtk_widget_show(byte_scrollw);
718 #if GTK_MAJOR_VERSION < 2
719 byte_view = gtk_text_new(NULL, NULL);
720 gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
721 gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
722 gtk_text_set_line_wrap(GTK_TEXT(byte_view), FALSE);
724 byte_view = gtk_text_view_new();
725 gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
726 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
727 buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
728 style = gtk_widget_get_style(GTK_WIDGET(byte_view));
729 gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
730 gtk_text_buffer_create_tag(buf, "reverse",
731 "font-desc", user_font_get_regular(),
732 "foreground-gdk", &style->text[GTK_STATE_SELECTED],
733 "background-gdk", &style->base[GTK_STATE_SELECTED],
735 gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
737 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TVBUFF_KEY, tvb);
738 gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
740 SIGNAL_CONNECT(byte_view, "show", byte_view_realize_cb, NULL);
741 SIGNAL_CONNECT(byte_view, "button_press_event", byte_view_button_press_cb,
742 OBJECT_GET_DATA(popup_menu_object, PM_HEXDUMP_KEY));
744 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_PTR, tree);
745 OBJECT_SET_DATA(byte_view, E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
747 gtk_widget_show(byte_view);
749 /* no tabs if this is the first page */
750 if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
751 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
753 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
755 /* set this page (this will print the packet data) */
756 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb),
757 gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
763 add_main_byte_views(epan_dissect_t *edt)
765 add_byte_views(edt, tree_view, byte_nb_ptr);
769 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
770 GtkWidget *byte_nb_ptr)
776 * Get rid of all the old notebook tabs.
778 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
779 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
782 * Add to the specified byte view notebook tabs for hex dumps
783 * of all the data sources for the specified frame.
785 for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
787 add_byte_tab(byte_nb_ptr, src->name, src->tvb, edt->tree,
792 * Initially select the first byte view.
794 gtk_notebook_set_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
799 static GtkWidget *savehex_dlg=NULL;
802 savehex_dlg_destroy_cb(void)
808 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_)
813 const guint8 *data_p = NULL;
814 GString *ASCII_representation = g_string_new("");
815 GString *byte_str = g_string_new("");
817 bv = get_notebook_bv_ptr(byte_nb_ptr);
819 /* shouldn't happen */
820 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
824 data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
826 g_string_sprintfa(byte_str,"%04x ",i); /* Offset 0000 */
827 for (i=0; i<len; i++){
828 g_string_sprintfa(ASCII_representation,"%c",isprint(*data_p) ? *data_p : '.');
829 g_string_sprintfa(byte_str," %02x",*data_p++);
830 if ((i+1)%16==0 && i!=0){
831 g_string_sprintfa(byte_str," %s\n%04x ",ASCII_representation->str,i+1);
832 g_string_assign (ASCII_representation,"");
836 if(ASCII_representation->len){
837 for (i=ASCII_representation->len; i<16; i++){
838 g_string_sprintfa(byte_str," ");
840 g_string_sprintfa(byte_str," %s\n",ASCII_representation->str);
842 /* Now that we have the byte data, copy it into the default clipboard */
843 copy_to_clipboard(byte_str);
844 g_string_free(byte_str, TRUE); /* Free the memory */
845 g_string_free(ASCII_representation, TRUE); /* Free the memory */
848 /* save the current highlighted hex data */
850 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
853 int fd, start, end, len;
854 const guint8 *data_p = NULL;
855 const char *file = NULL;
857 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
858 file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
860 file = gtk_file_selection_get_filename(GTK_FILE_SELECTION(savehex_dlg));
863 if (!file ||! *file) {
864 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
868 /* Must check if file name exists first */
870 bv = get_notebook_bv_ptr(byte_nb_ptr);
872 /* shouldn't happen */
873 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
877 * Retrieve the info we need
879 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
880 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
881 data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
883 if (data_p == NULL || start == -1 || start > end) {
884 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
885 "No data selected to save!");
889 fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
891 open_failure_alert_box(file, errno, TRUE);
894 if (write(fd, data_p + start, end - start) < 0) {
895 write_failure_alert_box(file, errno);
900 write_failure_alert_box(file, errno);
904 /* Get rid of the dialog box */
905 window_destroy(GTK_WIDGET(savehex_dlg));
908 /* Launch the dialog box to put up the file selection box etc */
909 void savehex_cb(GtkWidget * w _U_, gpointer data _U_)
912 const guint8 *data_p = NULL;
919 /* don't show up the dialog, if no data has to be saved */
920 bv = get_notebook_bv_ptr(byte_nb_ptr);
922 /* shouldn't happen */
923 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
926 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
927 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
928 data_p = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
930 if (data_p == NULL || start == -1 || start > end) {
931 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
935 /* if the window is already open, bring it to front */
937 reactivate_window(savehex_dlg);
942 * Build the dialog box we need.
944 savehex_dlg = file_selection_new("Ethereal: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
947 label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
948 end - start, plurality(end - start, "byte", "bytes"));
949 dlg_lb = gtk_label_new(label);
951 file_selection_set_extra_widget(savehex_dlg, dlg_lb);
952 gtk_widget_show(dlg_lb);
954 SIGNAL_CONNECT(savehex_dlg, "destroy", savehex_dlg_destroy_cb, NULL);
956 #if (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 4) || GTK_MAJOR_VERSION > 2
957 if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
958 savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
960 window_destroy(savehex_dlg);
963 /* Connect the ok_button to file_save_as_ok_cb function and pass along a
964 pointer to the file selection box widget */
965 SIGNAL_CONNECT(GTK_FILE_SELECTION (savehex_dlg)->ok_button, "clicked",
966 savehex_save_clicked_cb, savehex_dlg);
968 window_set_cancel_button(savehex_dlg,
969 GTK_FILE_SELECTION(savehex_dlg)->cancel_button, window_cancel_button_cb);
971 SIGNAL_CONNECT(savehex_dlg, "delete_event", window_delete_event_cb, NULL);
973 gtk_file_selection_set_filename(GTK_FILE_SELECTION(savehex_dlg), "");
975 gtk_widget_show_all(savehex_dlg);
976 window_present(savehex_dlg);
982 /* Update the progress bar this many times when reading a file. */
983 #define N_PROGBAR_UPDATES 100
987 * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
988 * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
989 * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
990 * presumably why there's a progress bar for it.)
992 * Perhaps what's needed is a custom widget (either one that lets you stuff
993 * text into it more quickly, or one that's a "virtual" widget so that the
994 * text for a row is constructed, via a callback, when the row is to be
995 * displayed). A custom widget might also let us treat the offset, hex
996 * data, and ASCII data as three columns, so you can select purely in
997 * the hex dump column.
999 #if GTK_MAJOR_VERSION < 2
1001 packet_hex_print_common(GtkText *bv, const guint8 *pd, int len, int bstart,
1002 int bend, int encoding)
1005 packet_hex_print_common(GtkTextView *bv, const guint8 *pd, int len, int bstart,
1006 int bend, int encoding)
1009 int i = 0, j, k, cur;
1010 guchar line[MAX_LINE_LEN + 1];
1011 static guchar hexchars[16] = {
1012 '0', '1', '2', '3', '4', '5', '6', '7',
1013 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1015 unsigned int use_digits;
1016 gboolean reverse, newreverse;
1017 #if GTK_MAJOR_VERSION < 2
1018 GdkFont *cur_font, *new_font;
1021 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(bv));
1023 const char *revstyle;
1026 GtkTextMark *mark = NULL;
1029 progdlg_t *progbar = NULL;
1031 gboolean progbar_stop_flag;
1032 GTimeVal progbar_start_time;
1033 gchar progbar_status_str[100];
1034 int progbar_nextstep;
1035 int progbar_quantum;
1037 #if GTK_MAJOR_VERSION < 2
1038 /* Freeze the text for faster display */
1039 gtk_text_freeze(bv);
1041 /* Clear out the text */
1042 gtk_text_set_point(bv, 0);
1043 /* Keep GTK+ 1.2.3 through 1.2.6 from dumping core - see
1044 http://www.ethereal.com/lists/ethereal-dev/199912/msg00312.html and
1045 http://www.gnome.org/mailing-lists/archives/gtk-devel-list/1999-October/0051.shtml
1046 for more information */
1047 gtk_adjustment_set_value(bv->vadj, 0.0);
1048 gtk_text_forward_delete(bv, gtk_text_get_length(bv));
1050 gtk_text_buffer_set_text(buf, "", 0);
1051 gtk_text_buffer_get_start_iter(buf, &iter);
1055 * How many of the leading digits of the offset will we supply?
1056 * We always supply at least 4 digits, but if the maximum offset
1057 * won't fit in 4 digits, we use as many digits as will be needed.
1059 if (((len - 1) & 0xF0000000) != 0)
1060 use_digits = 8; /* need all 8 digits */
1061 else if (((len - 1) & 0x0F000000) != 0)
1062 use_digits = 7; /* need 7 digits */
1063 else if (((len - 1) & 0x00F00000) != 0)
1064 use_digits = 6; /* need 6 digits */
1065 else if (((len - 1) & 0x000F0000) != 0)
1066 use_digits = 5; /* need 5 digits */
1068 use_digits = 4; /* we'll supply 4 digits */
1070 /* Record the number of digits in this text view. */
1071 OBJECT_SET_DATA(bv, E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
1073 /* Update the progress bar when it gets to this value. */
1074 progbar_nextstep = 0;
1075 /* When we reach the value that triggers a progress bar update,
1076 bump that value by this amount. */
1077 progbar_quantum = len/N_PROGBAR_UPDATES;
1079 progbar_stop_flag = FALSE;
1080 g_get_current_time(&progbar_start_time);
1083 /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
1084 when we update it, we have to run the GTK+ main loop to get it
1085 to repaint what's pending, and doing so may involve an "ioctl()"
1086 to see if there's any pending input from an X server, and doing
1087 that for every packet can be costly, especially on a big file. */
1088 if (i >= progbar_nextstep) {
1089 /* let's not divide by zero. I should never be started
1090 * with count == 0, so let's assert that
1093 progbar_val = (gfloat) i / len;
1095 if (progbar == NULL)
1096 /* Create the progress bar if necessary */
1097 progbar = delayed_create_progress_dlg("Processing", "Packet Details",
1099 &progbar_start_time,
1102 if (progbar != NULL) {
1103 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
1104 "%4u of %u bytes", i, len);
1105 update_progress_dlg(progbar, progbar_val, progbar_status_str);
1108 progbar_nextstep += progbar_quantum;
1111 if (progbar_stop_flag) {
1112 /* Well, the user decided to abort the operation. Just stop,
1113 and arrange to return TRUE to our caller, so they know it
1114 was stopped explicitly. */
1118 /* Print the line number */
1123 c = (i >> (j*4)) & 0xF;
1124 line[cur++] = hexchars[c];
1130 /* Display with inverse video ? */
1131 #if GTK_MAJOR_VERSION < 2
1132 if (prefs.gui_hex_dump_highlight_style) {
1133 gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1134 /* Do we start in reverse? */
1135 reverse = i >= bstart && i < bend;
1136 fg = reverse ? &WHITE : &BLACK;
1137 bg = reverse ? &BLACK : &WHITE;
1139 k = i + BYTE_VIEW_WIDTH;
1141 /* Print the hex bit */
1144 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1145 line[cur++] = hexchars[pd[i] & 0x0f];
1147 line[cur++] = ' '; line[cur++] = ' ';
1150 newreverse = i >= bstart && i < bend;
1151 /* Have we gone from reverse to plain? */
1152 if (reverse && (reverse != newreverse)) {
1153 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1158 /* Inter byte space if not at end of line */
1161 /* insert a space every BYTE_VIEW_SEP bytes */
1162 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1166 /* Have we gone from plain to reversed? */
1167 if (!reverse && (reverse != newreverse)) {
1168 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1173 reverse = newreverse;
1175 /* Print remaining part of line */
1176 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1178 /* Print some space at the end of the line */
1179 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1180 gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, cur);
1183 /* Print the ASCII bit */
1185 /* Do we start in reverse? */
1186 reverse = i >= bstart && i < bend;
1187 fg = reverse ? &WHITE : &BLACK;
1188 bg = reverse ? &BLACK : &WHITE;
1191 if (encoding == CHAR_ASCII) {
1194 else if (encoding == CHAR_EBCDIC) {
1195 c = EBCDIC_to_ASCII1(pd[i]);
1198 g_assert_not_reached();
1200 line[cur++] = isprint(c) ? c : '.';
1205 newreverse = i >= bstart && i < bend;
1206 /* Have we gone from reverse to plain? */
1207 if (reverse && (reverse != newreverse)) {
1208 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1214 /* insert a space every BYTE_VIEW_SEP bytes */
1215 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1219 /* Have we gone from plain to reversed? */
1220 if (!reverse && (reverse != newreverse)) {
1221 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1226 reverse = newreverse;
1228 /* Print remaining part of line */
1229 gtk_text_insert(bv, user_font_get_regular(), fg, bg, line, cur);
1233 gtk_text_insert(bv, user_font_get_regular(), &BLACK, &WHITE, line, -1);
1236 gtk_text_insert(bv, user_font_get_regular(), NULL, NULL, line, -1);
1237 /* Do we start in bold? */
1238 cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1240 k = i + BYTE_VIEW_WIDTH;
1242 /* Print the hex bit */
1245 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1246 line[cur++] = hexchars[pd[i] & 0x0f];
1248 line[cur++] = ' '; line[cur++] = ' ';
1252 /* insert a space every BYTE_VIEW_SEP bytes */
1253 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1254 /* Did we cross a bold/plain boundary? */
1255 new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1256 if (cur_font != new_font) {
1257 gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1258 cur_font = new_font;
1263 gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1267 /* Print the ASCII bit */
1268 cur_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1271 if (encoding == CHAR_ASCII) {
1274 else if (encoding == CHAR_EBCDIC) {
1275 c = EBCDIC_to_ASCII1(pd[i]);
1278 g_assert_not_reached();
1280 line[cur++] = isprint(c) ? c : '.';
1285 /* insert a space every BYTE_VIEW_SEP bytes */
1286 if( ( i % BYTE_VIEW_SEP ) == 0 ) line[cur++] = ' ';
1287 /* Did we cross a bold/plain boundary? */
1288 new_font = (i >= bstart && i < bend) ? user_font_get_bold() : user_font_get_regular();
1289 if (cur_font != new_font) {
1290 gtk_text_insert(bv, cur_font, NULL, NULL, line, cur);
1291 cur_font = new_font;
1297 gtk_text_insert(bv, cur_font, NULL, NULL, line, -1);
1300 if (prefs.gui_hex_dump_highlight_style)
1301 revstyle = "reverse";
1305 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, -1, "plain",
1307 /* Do we start in reverse? */
1308 reverse = i >= bstart && i < bend;
1310 k = i + BYTE_VIEW_WIDTH;
1312 /* Print the hex bit */
1315 line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
1316 line[cur++] = hexchars[pd[i] & 0x0f];
1318 line[cur++] = ' '; line[cur++] = ' ';
1321 newreverse = i >= bstart && i < bend;
1322 /* Have we gone from reverse to plain? */
1323 if (reverse && (reverse != newreverse)) {
1324 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1328 /* Inter byte space if not at end of line */
1331 /* insert a space every BYTE_VIEW_SEP bytes */
1332 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1336 /* Have we gone from plain to reversed? */
1337 if (!reverse && (reverse != newreverse)) {
1338 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1340 mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
1343 reverse = newreverse;
1345 /* Print remaining part of line */
1346 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1347 reverse ? revstyle : "plain",
1350 /* Print some space at the end of the line */
1351 line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
1352 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1356 /* Print the ASCII bit */
1358 /* Do we start in reverse? */
1359 reverse = i >= bstart && i < bend;
1362 if (encoding == CHAR_ASCII) {
1365 else if (encoding == CHAR_EBCDIC) {
1366 c = EBCDIC_to_ASCII1(pd[i]);
1369 g_assert_not_reached();
1371 line[cur++] = isprint(c) ? c : '.';
1376 newreverse = i >= bstart && i < bend;
1377 /* Have we gone from reverse to plain? */
1378 if (reverse && (reverse != newreverse)) {
1379 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1380 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1382 g_free( (gpointer) convline);
1386 /* insert a space every BYTE_VIEW_SEP bytes */
1387 if( ( i % BYTE_VIEW_SEP ) == 0 ) {
1391 /* Have we gone from plain to reversed? */
1392 if (!reverse && (reverse != newreverse)) {
1393 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1394 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1396 g_free( (gpointer) convline);
1399 reverse = newreverse;
1401 /* Print remaining part of line */
1402 convline = g_locale_to_utf8(line, cur, NULL, &newsize, NULL);
1403 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, convline, newsize,
1404 reverse ? revstyle : "plain",
1406 g_free( (gpointer) convline);
1409 gtk_text_buffer_insert_with_tags_by_name(buf, &iter, line, cur,
1414 /* We're done printing the packets; destroy the progress bar if
1416 if (progbar != NULL)
1417 destroy_progress_dlg(progbar);
1419 /* scroll text into position */
1420 #if GTK_MAJOR_VERSION < 2
1421 gtk_text_thaw(bv); /* must thaw before adjusting scroll bars */
1426 linenum = bstart / BYTE_VIEW_WIDTH;
1427 scrollval = MIN(linenum * user_font_get_regular_height(),
1428 bv->vadj->upper - bv->vadj->page_size);
1430 gtk_adjustment_set_value(bv->vadj, scrollval);
1434 gtk_text_view_scroll_to_mark(bv, mark, 0.0, TRUE, 1.0, 0.0);
1435 gtk_text_buffer_delete_mark(buf, mark);
1440 #if GTK_MAJOR_VERSION < 2
1442 packet_hex_print(GtkText *bv, const guint8 *pd, frame_data *fd,
1443 field_info *finfo, guint len)
1446 packet_hex_print(GtkTextView *bv, const guint8 *pd, frame_data *fd,
1447 field_info *finfo, guint len)
1450 /* do the initial printing and save the information needed */
1451 /* to redraw the display if preferences change. */
1453 int bstart, bend = -1, blen;
1455 if (finfo != NULL) {
1456 bstart = finfo->start;
1457 blen = finfo->length;
1462 if (bstart >= 0 && blen >= 0) {
1463 bend = bstart + blen;
1466 /* save the information needed to redraw the text */
1467 /* should we save the fd & finfo pointers instead ?? */
1468 OBJECT_SET_DATA(bv, E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bend));
1469 OBJECT_SET_DATA(bv, E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bstart));
1470 OBJECT_SET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY,
1471 GINT_TO_POINTER(fd->flags.encoding));
1473 packet_hex_print_common(bv, pd, len, bstart, bend, fd->flags.encoding);
1477 * Redraw the text using the saved information; usually called if
1478 * the preferences have changed.
1480 #if GTK_MAJOR_VERSION < 2
1482 packet_hex_reprint(GtkText *bv)
1485 packet_hex_reprint(GtkTextView *bv)
1488 int start, end, encoding;
1492 start = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_START_KEY));
1493 end = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_END_KEY));
1494 data = get_byte_view_data_and_length(GTK_WIDGET(bv), &len);
1495 g_assert(data != NULL);
1496 encoding = GPOINTER_TO_INT(OBJECT_GET_DATA(bv, E_BYTE_VIEW_ENCODE_KEY));
1498 packet_hex_print_common(bv, data, len, start, end, encoding);
1501 /* List of all protocol tree widgets, so we can globally set the selection
1502 mode and font of all of them. */
1503 static GList *ptree_widgets;
1505 /* Add a protocol tree widget to the list of protocol tree widgets. */
1506 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1509 remember_ptree_widget(GtkWidget *ptreew)
1511 ptree_widgets = g_list_append(ptree_widgets, ptreew);
1513 /* Catch the "destroy" event on the widget, so that we remove it from
1514 the list when it's destroyed. */
1515 SIGNAL_CONNECT(ptreew, "destroy", forget_ptree_widget, NULL);
1518 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1520 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1522 ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1525 /* Set the selection mode of a given packet tree window. */
1527 set_ptree_sel_browse(GtkWidget *tree, gboolean val)
1529 #if GTK_MAJOR_VERSION >= 2
1530 GtkTreeSelection *selection;
1532 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1534 /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
1535 I think "browse" in Ethereal makes more sense than "SINGLE" in
1538 #if GTK_MAJOR_VERSION < 2
1539 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1540 GTK_SELECTION_SINGLE);
1542 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1546 #if GTK_MAJOR_VERSION < 2
1547 gtk_clist_set_selection_mode(GTK_CLIST(tree),
1548 GTK_SELECTION_BROWSE);
1550 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1556 set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
1558 set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
1561 /* Set the selection mode of all packet tree windows. */
1563 set_ptree_sel_browse_all(gboolean val)
1565 g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
1568 #if GTK_MAJOR_VERSION < 2
1570 set_ptree_style_cb(gpointer data, gpointer user_data)
1572 gtk_widget_set_style((GtkWidget *)data, (GtkStyle *)user_data);
1576 set_ptree_font_cb(gpointer data, gpointer user_data)
1578 gtk_widget_modify_font((GtkWidget *)data,
1579 (PangoFontDescription *)user_data);
1584 set_ptree_font_all(FONT_TYPE *font)
1586 #if GTK_MAJOR_VERSION < 2
1589 style = gtk_style_new();
1590 gdk_font_unref(style->font);
1594 g_list_foreach(ptree_widgets, set_ptree_style_cb, style);
1596 /* Now nuke the old style and replace it with the new one. */
1597 gtk_style_unref(item_style);
1600 g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1605 gboolean colors_ok = FALSE;
1606 GdkColor expert_color_chat = { 0, 0xcc00, 0xcc00, 0xe000 }; /* a pale bluegrey */
1607 GdkColor expert_color_note = { 0, 0xa000, 0xff00, 0xff00 }; /* a bright turquoise */
1608 GdkColor expert_color_warn = { 0, 0xff00, 0xff00, 0 }; /* yellow */
1609 GdkColor expert_color_error = { 0, 0xff00, 0x5c00, 0x5c00 }; /* pale red */
1611 void proto_draw_colors_init(void)
1617 get_color(&expert_color_chat);
1618 get_color(&expert_color_note);
1619 get_color(&expert_color_warn);
1620 get_color(&expert_color_error);
1626 #if GTK_MAJOR_VERSION >= 2
1627 static void tree_cell_renderer(GtkTreeViewColumn *tree_column _U_,
1628 GtkCellRenderer *cell,
1629 GtkTreeModel *tree_model,
1635 gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1638 proto_draw_colors_init();
1641 /* for the various possible attributes, see:
1642 * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1644 * color definitions can be found at:
1645 * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1646 * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1649 * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1650 * weight/style: doesn't take any effect
1653 /* for each field, we have to reset the renderer attributes */
1654 g_object_set (cell, "foreground-set", FALSE, NULL);
1656 g_object_set (cell, "background", "white", NULL);
1657 g_object_set (cell, "background-set", TRUE, NULL);
1659 g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1660 g_object_set (cell, "underline-set", FALSE, NULL);
1662 /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1663 g_object_set (cell, "style-set", FALSE, NULL);*/
1665 /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1666 g_object_set (cell, "weight-set", FALSE, NULL);*/
1668 if(FI_GET_FLAG(fi, FI_GENERATED)) {
1669 /* we use "[...]" to mark generated items, no need to change things here */
1671 /* as some fonts don't support italic, don't use this */
1672 /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1673 g_object_set (cell, "style-set", TRUE, NULL);
1675 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1676 g_object_set (cell, "weight-set", TRUE, NULL);*/
1679 if(fi->hfinfo->type == FT_PROTOCOL) {
1680 g_object_set (cell, "background", "gray90", NULL);
1681 g_object_set (cell, "background-set", TRUE, NULL);
1682 /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1683 g_object_set (cell, "weight-set", TRUE, NULL);*/
1686 if(fi->hfinfo->type == FT_FRAMENUM) {
1687 g_object_set (cell, "foreground", "blue", NULL);
1688 g_object_set (cell, "foreground-set", TRUE, NULL);
1690 g_object_set (cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1691 g_object_set (cell, "underline-set", TRUE, NULL);
1694 if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1695 switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1697 g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1698 g_object_set (cell, "background-set", TRUE, NULL);
1701 g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1702 g_object_set (cell, "background-set", TRUE, NULL);
1705 g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1706 g_object_set (cell, "background-set", TRUE, NULL);
1709 g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1710 g_object_set (cell, "background-set", TRUE, NULL);
1713 g_assert_not_reached();
1720 #if GTK_MAJOR_VERSION >= 2
1722 tree_view_key_pressed_cb(GtkCTree *ctree _U_, GdkEventKey *event, gpointer user_data _U_)
1724 GtkTreeSelection* selection;
1727 GtkTreeModel* model;
1730 gboolean has_parent;
1733 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1738 if(!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1742 path = gtk_tree_model_get_path(model, &iter);
1747 if (event->keyval == GDK_Left) {
1748 expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree_view), path);
1750 /* subtree is expanded, collapse it */
1751 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree_view), path);
1754 /* subtree is already collapsed, jump to parent node */
1755 has_parent = gtk_tree_model_iter_parent(model, &parent, &iter);
1756 path = gtk_tree_model_get_path(model, &parent);
1760 gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree_view), path,
1761 NULL /* focus_column */,
1762 FALSE /* !start_editing */);
1766 if (event->keyval == GDK_Right) {
1767 /* try to expand the subtree */
1768 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, FALSE /* !open_all */);
1779 main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p)
1781 GtkWidget *tv_scrollw, *tree_view;
1782 #if GTK_MAJOR_VERSION >= 2
1783 GtkTreeStore *store;
1784 GtkCellRenderer *renderer;
1785 GtkTreeViewColumn *column;
1790 tv_scrollw = scrolled_window_new(NULL, NULL);
1791 #if GTK_MAJOR_VERSION >= 2
1792 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1796 #if GTK_MAJOR_VERSION < 2
1797 tree_view = ctree_new(1, 0);
1798 SIGNAL_CONNECT(tree_view, "key-press-event", toggle_tree, NULL );
1799 SIGNAL_CONNECT(tree_view, "tree-expand", expand_tree, NULL );
1800 SIGNAL_CONNECT(tree_view, "tree-collapse", collapse_tree, NULL );
1801 /* I need this next line to make the widget work correctly with hidden
1802 * column titles and GTK_SELECTION_BROWSE */
1803 gtk_clist_set_column_auto_resize( GTK_CLIST(tree_view), 0, TRUE );
1805 store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
1806 tree_view = tree_view_new(GTK_TREE_MODEL(store));
1807 g_object_unref(G_OBJECT(store));
1808 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1809 renderer = gtk_cell_renderer_text_new();
1810 g_object_set (renderer, "ypad", 0, NULL);
1811 col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1812 -1, "Name", renderer,
1814 column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1816 gtk_tree_view_column_set_cell_data_func(column,
1822 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1823 GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1824 SIGNAL_CONNECT(tree_view, "row-expanded", expand_tree, NULL);
1825 SIGNAL_CONNECT(tree_view, "row-collapsed", collapse_tree, NULL);
1826 SIGNAL_CONNECT(tree_view, "key-press-event", tree_view_key_pressed_cb, NULL );
1828 gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1829 set_ptree_sel_browse(tree_view, prefs->gui_ptree_sel_browse);
1830 #if GTK_MAJOR_VERSION < 2
1831 if(item_style == NULL) {
1832 item_style = gtk_style_new();
1833 gdk_font_unref(item_style->font);
1834 item_style->font = user_font_get_regular();
1837 gtk_widget_set_style(tree_view, item_style);
1839 gtk_widget_modify_font(tree_view, user_font_get_regular());
1841 remember_ptree_widget(tree_view);
1843 *tree_view_p = tree_view;
1848 #if GTK_MAJOR_VERSION < 2
1849 void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1851 void expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1855 for(i=0; i < num_tree_types; i++) {
1856 tree_is_expanded[i] = TRUE;
1858 #if GTK_MAJOR_VERSION < 2
1859 proto_tree_draw(protocol_tree, tree_view);
1860 gtk_ctree_expand_recursive(GTK_CTREE(tree_view), NULL);
1862 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1866 #if GTK_MAJOR_VERSION < 2
1867 void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view)
1869 void collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1873 for(i=0; i < num_tree_types; i++) {
1874 tree_is_expanded[i] = FALSE;
1876 #if GTK_MAJOR_VERSION < 2
1877 proto_tree_draw(protocol_tree, tree_view);
1879 gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1884 struct proto_tree_draw_info {
1885 #if GTK_MAJOR_VERSION < 2
1887 GtkCTreeNode *ctree_node;
1889 GtkTreeView *tree_view;
1895 main_proto_tree_draw(proto_tree *protocol_tree)
1897 proto_tree_draw(protocol_tree, tree_view);
1902 tree_view_follow_link(field_info *fi)
1904 if(fi->hfinfo->type == FT_FRAMENUM) {
1905 cf_goto_frame(&cfile, fi->value.value.integer);
1910 /* If the user selected a position in the tree view, try to find
1911 * the item in the GUI proto_tree that corresponds to that byte, and
1914 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1916 #if GTK_MAJOR_VERSION < 2
1924 if(gtk_clist_get_selection_info(GTK_CLIST(widget),
1925 (gint) (((GdkEventButton *)event)->x),
1926 (gint) (((GdkEventButton *)event)->y),
1929 ctree = GTK_CTREE(widget);
1931 node = gtk_ctree_node_nth(ctree, row);
1934 gtk_ctree_select(ctree, node);
1936 /* if that's a doubleclick, try to follow the link */
1937 if(event->type == GDK_2BUTTON_PRESS) {
1938 fi = gtk_ctree_node_get_row_data(ctree, node);
1939 tree_view_follow_link(fi);
1945 GtkTreeSelection *sel;
1948 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1949 (gint) (((GdkEventButton *)event)->x),
1950 (gint) (((GdkEventButton *)event)->y),
1951 &path, NULL, NULL, NULL))
1953 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1954 gtk_tree_selection_select_path(sel, path);
1956 /* if that's a doubleclick, try to follow the link */
1957 if(event->type == GDK_2BUTTON_PRESS) {
1958 GtkTreeModel *model;
1962 if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1963 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1964 tree_view_follow_link(fi);
1974 /* fill the whole protocol tree with the string values */
1976 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1978 #if GTK_MAJOR_VERSION >= 2
1979 GtkTreeStore *store;
1981 struct proto_tree_draw_info info;
1983 #if GTK_MAJOR_VERSION < 2
1984 info.ctree = GTK_CTREE(tree_view);
1985 info.ctree_node = NULL;
1987 gtk_clist_freeze(GTK_CLIST(tree_view));
1989 info.tree_view = GTK_TREE_VIEW(tree_view);
1992 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
1996 * Clear out any crud left over in the display of the protocol
1997 * tree, by removing all nodes from the tree.
1998 * This is how it's done in testgtk.c in GTK+.
2000 #if GTK_MAJOR_VERSION < 2
2001 gtk_clist_clear(GTK_CLIST(tree_view));
2003 gtk_tree_store_clear(store);
2006 proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
2008 #if GTK_MAJOR_VERSION < 2
2009 gtk_clist_thaw(GTK_CLIST(tree_view));
2014 /* fill a single protocol tree item with the string value */
2016 proto_tree_draw_node(proto_node *node, gpointer data)
2018 struct proto_tree_draw_info info;
2019 struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
2021 field_info *fi = PITEM_FINFO(node);
2022 gchar label_str[ITEM_LABEL_LENGTH];
2024 gboolean is_leaf, is_expanded;
2025 #if GTK_MAJOR_VERSION < 2
2026 GtkCTreeNode *parent;
2028 GtkTreeStore *store;
2033 if (PROTO_ITEM_IS_HIDDEN(node))
2036 /* was a free format label produced? */
2038 label_ptr = fi->rep->representation;
2040 else { /* no, make a generic label */
2041 label_ptr = label_str;
2042 proto_item_fill_label(fi, label_str);
2045 if (node->first_child != NULL) {
2047 g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
2048 if (tree_is_expanded[fi->tree_type]) {
2052 is_expanded = FALSE;
2057 is_expanded = FALSE;
2060 if(PROTO_ITEM_IS_GENERATED(node)) {
2061 label_ptr = g_strdup_printf("[%s]", label_ptr);
2064 #if GTK_MAJOR_VERSION < 2
2065 info.ctree = parent_info->ctree;
2066 parent = gtk_ctree_insert_node ( info.ctree, parent_info->ctree_node, NULL,
2067 &label_ptr, 5, NULL, NULL, NULL, NULL,
2068 is_leaf, is_expanded );
2070 gtk_ctree_node_set_row_data( GTK_CTREE(info.ctree), parent, fi );
2072 info.tree_view = parent_info->tree_view;
2073 store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
2074 gtk_tree_store_append(store, &iter, parent_info->iter);
2075 gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
2078 if(PROTO_ITEM_IS_GENERATED(node)) {
2083 #if GTK_MAJOR_VERSION < 2
2084 info.ctree_node = parent;
2088 proto_tree_children_foreach(node, proto_tree_draw_node, &info);
2089 #if GTK_MAJOR_VERSION >= 2
2090 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
2092 /* #if GTK_MINOR_VERSION >= 2 */
2093 gtk_tree_view_expand_to_path(info.tree_view, path);
2095 gtk_tree_view_expand_row(info.tree_view, path, FALSE);
2098 gtk_tree_view_collapse_row(info.tree_view, path);
2099 gtk_tree_path_free(path);
2105 * Clear the hex dump and protocol tree panes.
2108 clear_tree_and_hex_views(void)
2110 /* Clear the hex dump by getting rid of all the byte views. */
2111 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
2112 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
2114 /* Add a placeholder byte view so that there's at least something
2115 displayed in the byte view notebook. */
2116 add_byte_tab(byte_nb_ptr, "", NULL, NULL, tree_view);
2118 /* Clear the protocol tree by removing all nodes in the ctree.
2119 This is how it's done in testgtk.c in GTK+ */
2120 #if GTK_MAJOR_VERSION < 2
2121 gtk_clist_clear(GTK_CLIST(tree_view));
2123 gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))));