GTK+:
[metze/wireshark/wip.git] / ui / gtk / packet_panes.c
1 /* packet_panes.c
2  * Routines for GTK+ packet display (packet details and hex dump panes)
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * Jeff Foster,    2001/03/12,  added support for displaying named
11  *                              data sources as tabbed hex windows
12  *
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.
17  *
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.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include "config.h"
29
30 #include <ctype.h>
31
32 #ifdef HAVE_FCNTL_H
33 #include <fcntl.h>
34 #endif
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #include <gtk/gtk.h>
41 #include <gdk/gdkkeysyms.h>
42 #if GTK_CHECK_VERSION(3,0,0)
43 # include <gdk/gdkkeysyms-compat.h>
44 #endif
45
46 #include <string.h>
47
48 #include <epan/epan_dissect.h>
49
50 #include <epan/packet.h>
51 #include <epan/charsets.h>
52 #include <epan/prefs.h>
53 #include <epan/filesystem.h>
54
55 #include "../isprint.h"
56
57 #include "ui/alert_box.h"
58 #include "ui/last_open_dir.h"
59 #include "ui/progress_dlg.h"
60 #include "ui/recent.h"
61 #include "ui/simple_dialog.h"
62 #include "ui/ui_util.h"
63
64 #include <wsutil/file_util.h>
65
66 #include "ui/gtk/keys.h"
67 #include "ui/gtk/color_utils.h"
68 #include "ui/gtk/packet_win.h"
69 #include "ui/gtk/file_dlg.h"
70 #include "ui/gtk/gui_utils.h"
71 #include "ui/gtk/gtkglobals.h"
72 #include "ui/gtk/font_utils.h"
73 #include "ui/gtk/webbrowser.h"
74 #include "ui/gtk/main.h"
75 #include "ui/gtk/menus.h"
76 #include "ui/gtk/packet_panes.h"
77 #include "ui/gtk/proto_tree_model.h"
78 #include "ui/gtk/bytes_view.h"
79
80 #ifdef _WIN32
81 #include <gdk/gdkwin32.h>
82 #include <windows.h>
83 #include "ui/win32/file_dlg_win32.h"
84 #endif
85
86
87 #define E_BYTE_VIEW_TREE_PTR      "byte_view_tree_ptr"
88 #define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
89 #define E_BYTE_VIEW_TVBUFF_KEY    "byte_view_tvbuff"
90 #define E_BYTE_VIEW_START_KEY     "byte_view_start"
91 #define E_BYTE_VIEW_END_KEY       "byte_view_end"
92 #define E_BYTE_VIEW_MASK_KEY      "byte_view_mask"
93 #define E_BYTE_VIEW_MASKLE_KEY    "byte_view_mask_le"
94 #define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
95 #define E_BYTE_VIEW_APP_END_KEY   "byte_view_app_end"
96 #define E_BYTE_VIEW_ENCODE_KEY    "byte_view_encode"
97
98 /* Get the current text window for the notebook. */
99 GtkWidget *
100 get_notebook_bv_ptr(GtkWidget *nb_ptr)
101 {
102     int num;
103     GtkWidget *bv_page;
104
105     num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
106     bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
107     if (bv_page)
108         return gtk_bin_get_child(GTK_BIN(bv_page));
109     else
110         return NULL;
111 }
112
113 /*
114  * Get the data and length for a byte view, given the byte view page.
115  * Return the pointer, or NULL on error, and set "*data_len" to the length.
116  */
117 const guint8 *
118 get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
119 {
120     tvbuff_t *byte_view_tvb;
121     const guint8 *data_ptr;
122
123     byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
124     if (byte_view_tvb == NULL)
125         return NULL;
126
127     if ((*data_len = tvb_length(byte_view_tvb))) {
128         data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
129         return data_ptr;
130     } else
131         return "";
132 }
133
134 /*
135  * Set the current text window for the notebook to the window that
136  * refers to a particular tvbuff.
137  */
138 void
139 set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
140 {
141     int num;
142     GtkWidget *bv_page, *bv;
143     tvbuff_t *bv_tvb;
144
145     for (num = 0;
146          (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
147          num++) {
148         bv = gtk_bin_get_child(GTK_BIN(bv_page));
149         bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
150         if (bv_tvb == tvb) {
151             /* Found it. */
152             gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
153             break;
154         }
155     }
156 }
157
158 /* Redraw a given byte view window. */
159 void
160 redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
161 {
162     GtkWidget *bv;
163     const guint8 *data;
164     guint len;
165
166     bv = get_notebook_bv_ptr(nb);
167     if (bv != NULL) {
168         data = get_byte_view_data_and_length(bv, &len);
169         if (data != NULL)
170             packet_hex_print(bv, data, fd, finfo, len);
171     }
172 }
173
174 /* Redraw all byte view windows. */
175 void
176 redraw_packet_bytes_all(void)
177 {
178     if (cfile.current_frame != NULL)
179         redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
180
181     redraw_packet_bytes_packet_wins();
182
183     /* XXX - this is a hack, to workaround a bug in GTK2.x!
184        when changing the font size, even refilling of the corresponding
185        gtk_text_buffer doesn't seem to trigger an update.
186        The only workaround is to freshly select the frame, which will remove any
187        existing notebook tabs and "restart" the whole byte view again. */
188     if (cfile.current_frame != NULL) {
189         cfile.current_row = -1;
190         cf_goto_frame(&cfile, cfile.current_frame->num);
191     }
192 }
193
194 static void
195 expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
196             GtkTreePath *path _U_, gpointer user_data _U_)
197 {
198     field_info   *finfo;
199     GtkTreeModel *model;
200
201     model = gtk_tree_view_get_model(tree_view);
202     gtk_tree_model_get(model, iter, 1, &finfo, -1);
203     g_assert(finfo);
204
205     /* scroll the expanded item to reduce the need to do a manual scroll down
206      * and provide faster navigation of deeper trees */
207
208     if(prefs.gui_auto_scroll_on_expand) 
209         gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
210
211     /*
212      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
213      * are thus presumably leaf nodes and cannot be expanded.
214      */
215     if (finfo->tree_type != -1) {
216         g_assert(finfo->tree_type >= 0 &&
217                  finfo->tree_type < num_tree_types);
218         tree_is_expanded[finfo->tree_type] = TRUE;
219     }
220 }
221
222 static void
223 collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
224               GtkTreePath *path _U_, gpointer user_data _U_)
225 {
226     field_info   *finfo;
227     GtkTreeModel *model;
228
229     model = gtk_tree_view_get_model(tree_view);
230     gtk_tree_model_get(model, iter, 1, &finfo, -1);
231     g_assert(finfo);
232
233     /*
234      * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
235      * are thus presumably leaf nodes and cannot be collapsed.
236      */
237     if (finfo->tree_type != -1) {
238         g_assert(finfo->tree_type >= 0 &&
239                  finfo->tree_type < num_tree_types);
240         tree_is_expanded[finfo->tree_type] = FALSE;
241     }
242 }
243
244 struct field_lookup_info {
245     field_info  *fi;
246     GtkTreeIter  iter;
247 };
248
249 static gboolean
250 lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
251              gpointer data)
252 {
253     field_info *fi;
254     struct field_lookup_info *fli = (struct field_lookup_info *)data;
255
256     gtk_tree_model_get(model, iter, 1, &fi, -1);
257     if (fi == fli->fi) {
258         fli->iter = *iter;
259         return TRUE;
260     }
261     return FALSE;
262 }
263
264 GtkTreePath
265 *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
266 {
267     GtkTreeModel *model;
268     struct field_lookup_info fli;
269
270     g_assert(finfo != NULL);
271
272     model = gtk_tree_view_get_model(tree_view);
273     fli.fi = finfo;
274     gtk_tree_model_foreach(model, lookup_finfo, &fli);
275
276     return gtk_tree_model_get_path(model, &fli.iter);
277 }
278
279 /* If the user selected a certain byte in the byte view, try to find
280  * the item in the GUI proto_tree that corresponds to that byte, and:
281  *
282  *    if we succeed, select it, and return TRUE;
283  *    if we fail, return FALSE. */
284 gboolean
285 byte_view_select(GtkWidget *widget, GdkEventButton *event)
286 {
287     proto_tree   *tree;
288     GtkTreeView  *tree_view;
289     int           byte = -1;
290     tvbuff_t     *tvb;
291
292     tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
293     if (tree == NULL) {
294         /*
295          * Somebody clicked on the dummy byte view; do nothing.
296          */
297         return FALSE;
298     }
299     tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
300                                               E_BYTE_VIEW_TREE_VIEW_PTR));
301
302     byte = bytes_view_byte_from_xy(BYTES_VIEW(widget), (gint) event->x, (gint) event->y);
303
304     if (byte == -1) {
305         return FALSE;
306     }
307
308     /* Get the data source tvbuff */
309     tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
310
311     return highlight_field(tvb, byte, tree_view, tree);
312 }
313
314 /* This highlights the field in the proto tree that is at position byte */
315 gboolean
316 highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
317                 proto_tree *tree)
318 {
319     GtkTreeModel *model = NULL;
320     GtkTreePath  *first_path = NULL, *path = NULL;
321     GtkTreeIter   parent;
322     field_info   *finfo = NULL;
323     match_data    mdata;
324     struct field_lookup_info fli;
325
326     if (cfile.search_in_progress && cfile.string && cfile.decode_data) {
327         /* The tree where the target string matched one of the labels was discarded in
328            match_protocol_tree() so we have to search again in the latest tree. (Uugh) */
329         if (cf_find_string_protocol_tree(&cfile, tree, &mdata)) {
330             finfo = mdata.finfo;
331         }
332     } else {
333         /* Find the finfo that corresponds to our byte. */
334         finfo = proto_find_field_from_offset(tree, byte, tvb);
335     }
336
337     if (!finfo) {
338         return FALSE;
339     }
340
341     model = gtk_tree_view_get_model(tree_view);
342     fli.fi = finfo;
343     gtk_tree_model_foreach(model, lookup_finfo, &fli);
344
345     /* Expand our field's row */
346     first_path = gtk_tree_model_get_path(model, &fli.iter);
347     gtk_tree_view_expand_row(tree_view, first_path, FALSE);
348     expand_tree(tree_view, &fli.iter, NULL, NULL);
349
350     /* ... and its parents */
351     while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
352         path = gtk_tree_model_get_path(model, &parent);
353         gtk_tree_view_expand_row(tree_view, path, FALSE);
354         expand_tree(tree_view, &parent, NULL, NULL);
355         fli.iter = parent;
356         gtk_tree_path_free(path);
357     }
358
359     /* Refresh the display so that the expanded trees are visible */
360     proto_tree_draw(tree, GTK_WIDGET(tree_view));
361
362     /* select our field's row */
363     gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
364                                    first_path);
365
366     /* If the last search was a string or hex search within "Packet data", the entire field might
367        not be highlighted. If the user just clicked on one of the bytes comprising that field, the
368        above call didn't trigger a 'gtk_tree_view_get_selection' event. Call redraw_packet_bytes()
369        to make the highlighting of the entire field visible. */
370     if (!cfile.search_in_progress) {
371         if (cfile.hex || (cfile.string && cfile.packet_data)) {
372             redraw_packet_bytes(byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
373         }
374     }
375
376     /* And position the window so the selection is visible.
377      * Position the selection in the middle of the viewable
378      * pane. */
379     gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
380
381     gtk_tree_path_free(first_path);
382
383     return TRUE;
384 }
385
386 /* Calls functions for different mouse-button presses. */
387 static gboolean
388 byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
389 {
390     GdkEventButton *event_button = NULL;
391
392     if(widget == NULL || event == NULL || data == NULL) {
393         return FALSE;
394     }
395
396     if(event->type == GDK_BUTTON_PRESS) {
397         event_button = (GdkEventButton *) event;
398
399         /* To qoute the "Gdk Event Structures" doc:
400          * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
401         switch(event_button->button) {
402
403         case 1:
404             return byte_view_select(widget, event_button);
405         case 3:
406             return popup_menu_handler(widget, event, data);
407         default:
408             return FALSE;
409         }
410     }
411
412     return FALSE;
413 }
414
415 GtkWidget *
416 byte_view_new(void)
417 {
418     GtkWidget *byte_nb;
419
420     byte_nb = gtk_notebook_new();
421     gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
422
423     /* this will only have an effect, if no tabs are shown */
424     gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
425
426     /* set the tabs scrollable, if they don't fit into the pane */
427     gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
428
429     /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
430     gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
431
432     /* Add a placeholder byte view so that there's at least something
433        displayed in the byte view notebook. */
434     add_byte_tab(byte_nb, "", NULL, NULL, NULL);
435
436     return byte_nb;
437 }
438
439 static void
440 byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
441 {
442     const guint8 *byte_data;
443     guint byte_len;
444
445     byte_data = get_byte_view_data_and_length(bv, &byte_len);
446     if (byte_data == NULL) {
447         /* This must be the dummy byte view if no packet is selected. */
448         return;
449     }
450     packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
451 }
452
453 GtkWidget *
454 add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
455              proto_tree *tree, GtkWidget *tree_view)
456 {
457     GtkWidget *byte_view, *byte_scrollw, *label;
458
459     /* Byte view.  Create a scrolled window for the text. */
460     byte_scrollw = scrolled_window_new(NULL, NULL);
461     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
462                                         GTK_SHADOW_IN);
463     /* Add scrolled pane to tabbed window */
464     label = gtk_label_new(name);
465     gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
466
467     gtk_widget_show(byte_scrollw);
468
469     byte_view = bytes_view_new();
470     bytes_view_set_font(BYTES_VIEW(byte_view), user_font_get_regular());
471
472     g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
473     gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
474
475     g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
476     g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
477                      g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
478
479     g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
480     g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
481
482     gtk_widget_show(byte_view); /* triggers byte_view_realize_cb which calls packet_hex_print */
483
484     /* no tabs if this is the first page */
485     if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
486         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
487     else
488         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
489
490     /* set this page */
491     gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
492                                   gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
493
494     return byte_view;
495 }
496
497 void
498 add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
499                GtkWidget *byte_nb_ptr)
500 {
501     GSList *src_le;
502     struct data_source *src;
503
504     /*
505      * Get rid of all the old notebook tabs.
506      */
507     while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
508         gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
509
510     /*
511      * Add to the specified byte view notebook tabs for hex dumps
512      * of all the data sources for the specified frame.
513      */
514     for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
515         src = src_le->data;
516         add_byte_tab(byte_nb_ptr, get_data_source_name(src), get_data_source_tvb(src), edt->tree,
517                      tree_view);
518     }
519
520     /*
521      * Initially select the first byte view.
522      */
523     gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
524 }
525
526
527
528 static GtkWidget *savehex_dlg=NULL;
529
530 static void
531 savehex_dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
532 {
533     savehex_dlg = NULL;
534 }
535
536
537 static void
538 copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
539 {
540     const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
541     int i, j;
542     gboolean end_of_line = TRUE; /* Initial state is end of line */
543     int byte_line_part_length;
544
545     GString* hex_str;
546     GString* char_str;
547
548     /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
549     hex_str = g_string_new("");
550     char_str= g_string_new("");
551
552     i = 0;
553     while (i<data_len) {
554         if(end_of_line) {
555             g_string_append_printf(hex_str,"%04x  ",i); /* Offset - note that we _append_ here */
556         }
557
558         g_string_append_printf(hex_str," %02x",*data_p);
559         if(append_text) {
560             g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
561         }
562
563         ++data_p;
564
565         /* Look ahead to see if this is the end of the data */
566         byte_line_part_length = (++i) % byte_line_length;
567         if(i == data_len){
568             /* End of data - need to fill in spaces in hex string and then do "end of line".
569              *
570              */
571             for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
572                 g_string_append(hex_str,"   "); /* Three spaces for each missing byte */
573             }
574             end_of_line = TRUE;
575         } else {
576             end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
577         }
578
579
580         if (end_of_line){
581             /* End of line */
582             g_string_append(copy_buffer, hex_str->str);
583             if(append_text) {
584                 /* Two spaces between hex and text */
585                 g_string_append_c(copy_buffer, ' ');
586                 g_string_append_c(copy_buffer, ' ');
587                 g_string_append(copy_buffer, char_str->str);
588             }
589             /* Setup ready for next line */
590             g_string_assign(char_str,"");
591             g_string_assign(hex_str, "\n");
592         }
593     }
594
595     g_string_free(hex_str, TRUE);
596     g_string_free(char_str, TRUE);
597 }
598
599 static int
600 copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
601 {
602
603     gchar to_append;
604
605     /* Copy printable characters, newlines, and (horizontal) tabs. */
606     if(isprint(*data_p)) {
607         to_append = *data_p;
608     } else if(*data_p==0x0a) {
609         to_append = '\n';
610     } else if(*data_p==0x09) {
611         to_append = '\t';
612     } else {
613         return 1; /* Just ignore non-printable bytes */
614     }
615     g_string_append_c(copy_buffer,to_append);
616     return 1;
617 }
618
619 static
620 int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
621 {
622     g_string_append_printf(copy_buffer, "%02x", *data_p);
623     return 1;
624 }
625
626 void
627 copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
628 {
629     GtkWidget *bv;
630
631     guint len = 0;
632     int bytes_consumed = 0;
633     int flags;
634
635     const guint8* data_p;
636
637     GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
638
639     bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
640     if (bv == NULL) {
641         /* shouldn't happen */
642         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
643         return;
644     }
645
646     data_p = get_byte_view_data_and_length(bv, &len);
647     g_assert(data_p != NULL);
648
649     flags = data_type & CD_FLAGSMASK;
650     data_type = data_type & CD_TYPEMASK;
651
652     if(flags & CD_FLAGS_SELECTEDONLY) {
653         int start, end;
654
655         /* Get the start and end of the highlighted bytes. */
656         start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
657         end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
658
659         if(start >= 0 && end > start && (end - start <= (int)len)) {
660             len = end - start;
661             data_p += start;
662         }
663     }
664
665     switch(data_type) {
666     case(CD_ALLINFO):
667         /* This is too different from other text formats - handle separately */
668         copy_hex_all_info(copy_buffer, data_p, len, TRUE);
669         break;
670     case(CD_HEXCOLUMNS):
671         /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
672         copy_hex_all_info(copy_buffer, data_p, len, FALSE);
673         break;
674     case(CD_BINARY):
675         /* Completely different logic to text copies - leave copy buffer alone */
676         copy_binary_to_clipboard(data_p,len);
677         break;
678     default:
679         /* Incrementally write to text buffer in various formats */
680         while (len > 0){
681             switch(data_type) {
682             case (CD_TEXTONLY):
683                 bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
684                 break;
685             case (CD_HEX):
686                 bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
687                 break;
688             default:
689                 g_assert_not_reached();
690                 break;
691             }
692
693             g_assert(bytes_consumed>0);
694             data_p += bytes_consumed;
695             len -= bytes_consumed;
696         }
697         break;
698     }
699
700     if(copy_buffer->len > 0) {
701         copy_to_clipboard(copy_buffer);
702     }
703
704     g_string_free(copy_buffer, TRUE);
705 }
706
707 /* save the current highlighted hex data */
708 static gboolean
709 savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
710 {
711     GtkWidget *bv;
712     int fd, start, end;
713     guint len;
714     const guint8 *data_p = NULL;
715     char *file;
716
717     file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
718
719 #if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
720     if (!file ||! *file) {
721         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
722         g_free(file);
723         return TRUE;
724     }
725 #endif
726     if (test_for_directory(file) == EISDIR) {
727         /* It's a directory - set the file selection box to display that
728            directory, and leave the selection box displayed. */
729         set_last_open_dir(file);
730         g_free(file);
731         file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
732         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
733         return FALSE; /* do gtk_dialog_run again */
734     }
735
736     /* XXX: Must check if file name exists first */
737
738     bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
739     if (bv == NULL) {
740         /* shouldn't happen */
741         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
742         g_free(file);
743         return TRUE;
744     }
745     /*
746      * Retrieve the info we need
747      */
748     start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
749     end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
750     data_p = get_byte_view_data_and_length(bv, &len);
751
752     if (data_p == NULL || start == -1 || start > end) {
753         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
754                       "No data selected to save!");
755         g_free(file);
756         return TRUE;
757     }
758
759     fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
760     if (fd == -1) {
761         open_failure_alert_box(file, errno, TRUE);
762         g_free(file);
763         return TRUE;
764     }
765     if (ws_write(fd, data_p + start, end - start) < 0) {
766         write_failure_alert_box(file, errno);
767         ws_close(fd);
768         g_free(file);
769         return TRUE;
770     }
771     if (ws_close(fd) < 0) {
772         write_failure_alert_box(file, errno);
773         g_free(file);
774         return TRUE;
775     }
776
777     /* Get rid of the dialog box */
778     g_free(file);
779 #if 0 /* being handled by caller  (for now) */
780     window_destroy(GTK_WIDGET(savehex_dlg));
781 #endif
782     return TRUE;
783 }
784
785 /* Launch the dialog box to put up the file selection box etc */
786 #ifdef _WIN32
787 void
788 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
789 {
790     win32_export_raw_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
791     return;
792 }
793 #else
794 void
795 savehex_cb(GtkWidget * w _U_, gpointer data _U_)
796 {
797     int start, end;
798     guint len;
799     const guint8 *data_p = NULL;
800     gchar *label;
801     GtkWidget   *bv;
802     GtkWidget   *dlg_lb;
803
804     /* don't show up the dialog, if no data has to be saved */
805     bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
806     if (bv == NULL) {
807         /* shouldn't happen */
808         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
809         return;
810     }
811     start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
812     end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
813     data_p = get_byte_view_data_and_length(bv, &len);
814
815     if (data_p == NULL || start == -1 || start > end) {
816         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
817         return;
818     }
819
820 #if 0  /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
821     /* if the window is already open, bring it to front */
822     if(savehex_dlg){
823         reactivate_window(savehex_dlg);
824         return;
825     }
826 #endif
827     /*
828      * Build the dialog box we need.
829      */
830     savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
831     gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
832
833     /* label */
834     label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
835                             end - start, plurality(end - start, "byte", "bytes"));
836     dlg_lb = gtk_label_new(label);
837     g_free(label);
838     file_selection_set_extra_widget(savehex_dlg, dlg_lb);
839     gtk_widget_show(dlg_lb);
840
841     g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
842
843 #if 0
844     if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
845         savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
846     } else {
847         window_destroy(savehex_dlg);
848     }
849 #endif
850     /* "Run" the GtkFileChooserDialog.                                              */
851     /* Upon exit: If "Accept" run the OK callback.                                  */
852     /*            If the OK callback returns with a FALSE status, re-run the dialog.*/
853     /*            If not accept (ie: cancel) destroy the window.                    */
854     /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must*    */
855     /*      return with a TRUE status so that the dialog window will be destroyed.  */
856     /*      Trying to re-run the dialog after popping up an alert box will not work */
857     /*       since the user will not be able to dismiss the alert box.              */
858     /*      The (somewhat unfriendly) effect: the user must re-invoke the           */
859     /*      GtkFileChooserDialog whenever the OK callback pops up an alert box.     */
860     /*                                                                              */
861     /*      ToDo: use GtkFileChooserWidget in a dialog window instead of            */
862     /*            GtkFileChooserDialog.                                             */
863     while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
864         if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
865             break; /* we're done */
866         }
867     }
868     window_destroy(savehex_dlg);
869 }
870 #endif
871
872 static void
873 packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
874                   int bend, guint32 bmask, int bmask_le,
875                   int astart, int aend, int encoding)
876 {
877         bytes_view_set_encoding(BYTES_VIEW(bv), encoding);
878         bytes_view_set_format(BYTES_VIEW(bv), recent.gui_bytes_view);
879         bytes_view_set_data(BYTES_VIEW(bv), pd, len);
880  
881         bytes_view_set_highlight_style(BYTES_VIEW(bv), prefs.gui_hex_dump_highlight_style);
882  
883         bytes_view_set_highlight(BYTES_VIEW(bv), bstart, bend, bmask, bmask_le);
884         bytes_view_set_highlight_appendix(BYTES_VIEW(bv), astart, aend);
885  
886         if (bstart != -1 && bend != -1)
887                 bytes_view_scroll_to_byte(BYTES_VIEW(bv), bstart);
888         bytes_view_refresh(BYTES_VIEW(bv));
889 }
890
891 void
892 packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
893                  field_info *finfo, guint len)
894 {
895     /* do the initial printing and save the information needed  */
896     /* to redraw the display if preferences change.             */
897
898     int bstart = -1, bend = -1, blen = -1;
899     guint32 bmask = 0x00; int bmask_le = 0;
900     int astart = -1, aend = -1, alen = -1;
901
902
903     if (finfo != NULL) {
904
905         if (cfile.search_in_progress && (cfile.hex || (cfile.string && cfile.packet_data))) {
906             /* In the hex view, only highlight the target bytes or string. The entire
907                field can then be displayed by clicking on any of the bytes in the field. */
908             if (cfile.hex) {
909                 blen = (int)strlen(cfile.sfilter)/2;
910             } else {
911                 blen = (int)strlen(cfile.sfilter);
912             }
913             bstart = cfile.search_pos - (blen-1);
914
915         } else {
916             blen = finfo->length;
917             bstart = finfo->start;
918         }
919
920         /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
921         if (finfo->hfinfo) bmask = finfo->hfinfo->bitmask;
922         astart = finfo->appendix_start;
923         alen = finfo->appendix_length;
924
925         if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
926             bmask_le = 1;
927         else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
928             bmask_le = 0;
929         else { /* unknown endianess - disable mask
930                   bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
931                */
932             bmask = 0x00;
933         }
934
935         if (bmask == 0x00) {
936             int bito = FI_GET_BITS_OFFSET(finfo);
937             int bitc = FI_GET_BITS_SIZE(finfo);
938             int bitt = bito + bitc;
939
940             /* construct mask using bito & bitc */
941             /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
942             if (bitt > 0 && bitt < 32) {
943
944                 bmask = ((1 << bitc) - 1) << ((8-bitt) & 7);
945                 bmask_le = 0; /* ? */
946             }
947         }
948     }
949
950     if (bstart >= 0 && blen > 0 && (guint)bstart < len) {
951         bend = bstart + blen;
952     }
953     if (astart >= 0 && alen > 0 && (guint)astart < len) {
954         aend = astart + alen;
955     }
956
957     if (bend == -1 && aend != -1) {
958         bstart = astart;
959         bmask = 0x00;
960         bend = aend;
961         astart = aend = -1;
962     }
963
964     /* don't exceed the end of available data */
965     if (aend != -1 && (guint)aend > len) aend = len;
966     if (bend != -1 && (guint)bend > len) bend = len;
967
968     /* save the information needed to redraw the text */
969     /* should we save the fd & finfo pointers instead ?? */
970     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
971     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
972     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
973     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
974     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
975     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
976     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
977                       GUINT_TO_POINTER((guint)fd->flags.encoding));
978
979     /* stig: it should be done only for bitview... */
980     if (recent.gui_bytes_view != BYTES_BITS)
981         bmask = 0x00;
982     packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
983 }
984
985 void
986 packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd, int offset, int bitoffset, guint len)
987 {
988     /* do the initial printing and save the information needed  */
989     /* to redraw the display if preferences change.             */
990
991     int bstart = offset, bend = (bstart != -1) ? offset+1 : -1;
992     guint32 bmask=0; int bmask_le = 0;
993     int astart = -1, aend = -1;
994
995     switch (recent.gui_bytes_view) {
996     case BYTES_HEX:
997         bmask = (bitoffset == 0) ? 0xf0 : (bitoffset == 4) ? 0x0f : 0xff;
998         break;
999
1000     case BYTES_BITS:
1001         bmask = (1 << (7-bitoffset));
1002         break;
1003
1004     default:
1005         g_assert_not_reached();
1006         break;
1007     }
1008
1009     /* save the information needed to redraw the text */
1010     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
1011     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
1012     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
1013     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
1014     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
1015     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
1016     g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
1017                       GUINT_TO_POINTER((guint)fd->flags.encoding));
1018
1019     packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
1020 }
1021
1022 /*
1023  * Redraw the text using the saved information; usually called if
1024  * the preferences have changed.
1025  */
1026 void
1027 packet_hex_reprint(GtkWidget *bv)
1028 {
1029     int start, end, mask, mask_le, encoding;
1030     int astart, aend;
1031     const guint8 *data;
1032     guint len = 0;
1033
1034     start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
1035     end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
1036     mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
1037     mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
1038     astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
1039     aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
1040     data = get_byte_view_data_and_length(bv, &len);
1041     g_assert(data != NULL);
1042     encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
1043
1044     /* stig: it should be done only for bitview... */
1045     if (recent.gui_bytes_view != BYTES_BITS)
1046         mask = 0x00;
1047     packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
1048 }
1049
1050 /* List of all protocol tree widgets, so we can globally set the selection
1051    mode and font of all of them. */
1052 static GList *ptree_widgets;
1053
1054 /* Add a protocol tree widget to the list of protocol tree widgets. */
1055 static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
1056
1057 static void
1058 remember_ptree_widget(GtkWidget *ptreew)
1059 {
1060     ptree_widgets = g_list_append(ptree_widgets, ptreew);
1061
1062     /* Catch the "destroy" event on the widget, so that we remove it from
1063        the list when it's destroyed. */
1064     g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
1065 }
1066
1067 /* Remove a protocol tree widget from the list of protocol tree widgets. */
1068 static void
1069 forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
1070 {
1071     ptree_widgets = g_list_remove(ptree_widgets, ptreew);
1072 }
1073
1074 static void
1075 set_ptree_font_cb(gpointer data, gpointer user_data)
1076 {
1077 #if GTK_CHECK_VERSION(3,0,0)
1078     gtk_widget_override_font((GtkWidget *)data,
1079                            (PangoFontDescription *)user_data);
1080 #else
1081     gtk_widget_modify_font((GtkWidget *)data,
1082                            (PangoFontDescription *)user_data);
1083 #endif
1084 }
1085
1086 void
1087 set_ptree_font_all(PangoFontDescription *font)
1088 {
1089     g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
1090 }
1091
1092
1093 /*
1094  * Each expert_color_* level below should match the light gradient
1095  * colors in image/expert_indicators.svg.
1096  */
1097 static gboolean colors_ok = FALSE;
1098
1099 GdkColor        expert_color_comment    = { 0, 0xb7b7, 0xf7f7, 0x7474 };        /* Green */
1100 GdkColor        expert_color_chat       = { 0, 0x8080, 0xb7b7, 0xf7f7 };        /* light blue */
1101 GdkColor        expert_color_note       = { 0, 0xa0a0, 0xffff, 0xffff };        /* bright turquoise */
1102 GdkColor        expert_color_warn       = { 0, 0xf7f7, 0xf2f2, 0x5353 };        /* yellow */
1103 GdkColor        expert_color_error      = { 0, 0xffff, 0x5c5c, 0x5c5c };        /* pale red */
1104 GdkColor        expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 };        /* black */
1105 GdkColor        hidden_proto_item       = { 0, 0x4444, 0x4444, 0x4444 };        /* gray */
1106
1107 gchar *expert_color_comment_str;
1108 gchar *expert_color_chat_str;
1109 gchar *expert_color_note_str;
1110 gchar *expert_color_warn_str;
1111 gchar *expert_color_error_str;
1112 gchar *expert_color_foreground_str;
1113
1114 void proto_draw_colors_init(void)
1115 {
1116     if(colors_ok) {
1117         return;
1118     }
1119 #if 0
1120     /* Allocating collor isn't necessary? */
1121     get_color(&expert_color_chat);
1122     get_color(&expert_color_note);
1123     get_color(&expert_color_warn);
1124     get_color(&expert_color_error);
1125     get_color(&expert_color_foreground);
1126 #endif
1127     expert_color_comment_str = gdk_color_to_string(&expert_color_comment);
1128     expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
1129     expert_color_note_str = gdk_color_to_string(&expert_color_note);
1130     expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
1131     expert_color_error_str = gdk_color_to_string(&expert_color_error);
1132     expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
1133
1134 #if 0
1135     get_color(&hidden_proto_item);
1136 #endif
1137     colors_ok = TRUE;
1138 }
1139
1140
1141 static void
1142 tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
1143                    GtkTreeModel *tree_model, GtkTreeIter *iter,
1144                    gpointer data _U_)
1145 {
1146     field_info   *fi;
1147
1148     gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
1149
1150     if(!colors_ok) {
1151         proto_draw_colors_init();
1152     }
1153
1154     /* for the various possible attributes, see:
1155      * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
1156      *
1157      * color definitions can be found at:
1158      * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
1159      * (a good color overview: http://www.computerhope.com/htmcolor.htm)
1160      *
1161      * some experiences:
1162      * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
1163      * weight/style: doesn't take any effect
1164      */
1165
1166     /* for each field, we have to reset the renderer attributes */
1167     g_object_set (cell, "foreground-set", FALSE, NULL);
1168
1169     g_object_set (cell, "background-set", FALSE, NULL);
1170
1171     g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
1172     g_object_set (cell, "underline-set", FALSE, NULL);
1173
1174     /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
1175     g_object_set (cell, "style-set", FALSE, NULL);*/
1176
1177     /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
1178     g_object_set (cell, "weight-set", FALSE, NULL);*/
1179
1180     if(FI_GET_FLAG(fi, FI_GENERATED)) {
1181         /* we use "[...]" to mark generated items, no need to change things here */
1182
1183         /* as some fonts don't support italic, don't use this */
1184         /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
1185         g_object_set (cell, "style-set", TRUE, NULL);
1186         */
1187         /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1188         g_object_set (cell, "weight-set", TRUE, NULL);*/
1189     }
1190
1191     if(FI_GET_FLAG(fi, FI_HIDDEN)) {
1192         g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
1193         g_object_set (cell, "foreground-set", TRUE, NULL);
1194     }
1195
1196     if (fi && fi->hfinfo) {
1197         if(fi->hfinfo->type == FT_PROTOCOL) {
1198             g_object_set (cell, "background", "gray90", NULL);
1199             g_object_set (cell, "background-set", TRUE, NULL);
1200             g_object_set (cell, "foreground", "black", NULL);
1201             g_object_set (cell, "foreground-set", TRUE, NULL);
1202             /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
1203             g_object_set (cell, "weight-set", TRUE, NULL);*/
1204         }
1205
1206         if((fi->hfinfo->type == FT_FRAMENUM) ||
1207            (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
1208             render_as_url(cell);
1209         }
1210     }
1211
1212     if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1213         switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
1214         case(PI_COMMENT):
1215             g_object_set (cell, "background-gdk", &expert_color_comment, NULL);
1216             g_object_set (cell, "background-set", TRUE, NULL);
1217             break;
1218         case(PI_CHAT):
1219             g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
1220             g_object_set (cell, "background-set", TRUE, NULL);
1221             break;
1222         case(PI_NOTE):
1223             g_object_set (cell, "background-gdk", &expert_color_note, NULL);
1224             g_object_set (cell, "background-set", TRUE, NULL);
1225             break;
1226         case(PI_WARN):
1227             g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
1228             g_object_set (cell, "background-set", TRUE, NULL);
1229             break;
1230         case(PI_ERROR):
1231             g_object_set (cell, "background-gdk", &expert_color_error, NULL);
1232             g_object_set (cell, "background-set", TRUE, NULL);
1233             break;
1234         default:
1235             g_assert_not_reached();
1236         }
1237         g_object_set (cell, "foreground", "black", NULL);
1238         g_object_set (cell, "foreground-set", TRUE, NULL);
1239     }
1240 }
1241
1242 GtkWidget *
1243 proto_tree_view_new(GtkWidget **tree_view_p)
1244 {
1245     GtkWidget *tv_scrollw, *tree_view;
1246     ProtoTreeModel *store;
1247     GtkCellRenderer *renderer;
1248     GtkTreeViewColumn *column;
1249     gint col_offset;
1250
1251     /* Tree view */
1252     tv_scrollw = scrolled_window_new(NULL, NULL);
1253     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
1254                                         GTK_SHADOW_IN);
1255
1256     store = proto_tree_model_new(NULL, prefs.display_hidden_proto_items);
1257     tree_view = tree_view_new(GTK_TREE_MODEL(store));
1258     g_object_unref(G_OBJECT(store));
1259     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
1260     renderer = gtk_cell_renderer_text_new();
1261     g_object_set (renderer, "ypad", 0, NULL);
1262     col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
1263                                                              -1, "Name", renderer,
1264                                                              "text", 0, NULL);
1265     column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
1266                                       col_offset - 1);
1267     gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
1268                                             NULL, NULL);
1269
1270     gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
1271                                     GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1272     g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
1273     g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
1274     gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
1275 #if GTK_CHECK_VERSION(3,0,0)
1276     gtk_widget_override_font(tree_view, user_font_get_regular());
1277 #else
1278     gtk_widget_modify_font(tree_view, user_font_get_regular());
1279 #endif
1280     remember_ptree_widget(tree_view);
1281
1282     *tree_view_p = tree_view;
1283
1284     return tv_scrollw;
1285 }
1286
1287 void
1288 expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1289 {
1290     int i;
1291     for(i=0; i < num_tree_types; i++) {
1292         tree_is_expanded[i] = TRUE;
1293     }
1294     gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
1295 }
1296
1297 void
1298 collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
1299 {
1300     int i;
1301     for(i=0; i < num_tree_types; i++) {
1302         tree_is_expanded[i] = FALSE;
1303     }
1304     gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
1305 }
1306
1307 static void
1308 tree_view_follow_link(field_info   *fi)
1309 {
1310     gchar *url;
1311
1312     if(fi->hfinfo->type == FT_FRAMENUM) {
1313         cf_goto_frame(&cfile, fi->value.value.uinteger);
1314     }
1315     if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
1316         url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL);
1317         if(url){
1318             browser_open_url(url);
1319             g_free(url);
1320         }
1321     }
1322 }
1323
1324
1325 /* If the user selected a position in the tree view, try to find
1326  * the item in the GUI proto_tree that corresponds to that byte, and
1327  * select it. */
1328 gboolean
1329 tree_view_select(GtkWidget *widget, GdkEventButton *event)
1330 {
1331     GtkTreeSelection    *sel;
1332     GtkTreePath         *path;
1333
1334     if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1335                                       (gint) (((GdkEventButton *)event)->x),
1336                                       (gint) (((GdkEventButton *)event)->y),
1337                                       &path, NULL, NULL, NULL))
1338     {
1339         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1340
1341         /* if that's a doubleclick, try to follow the link */
1342         if(event->type == GDK_2BUTTON_PRESS) {
1343             GtkTreeModel *model;
1344             GtkTreeIter iter;
1345             field_info   *fi;
1346
1347             if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
1348                 gtk_tree_model_get(model, &iter, 1, &fi, -1);
1349                 tree_view_follow_link(fi);
1350             }
1351         }
1352         else if (((GdkEventButton *)event)->button != 1) {
1353             /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
1354             gtk_tree_selection_select_path(sel, path);
1355         }
1356     } else {
1357         return FALSE;
1358     }
1359     return TRUE;
1360 }
1361
1362 static gboolean
1363 expand_finfos(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1364 {
1365     GtkTreeView *tree_view = (GtkTreeView *) data;
1366     field_info *fi;
1367
1368     if (!gtk_tree_model_iter_has_child(model, iter))
1369         return FALSE;
1370
1371     gtk_tree_model_get(model, iter, 1, &fi, -1);
1372
1373     g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
1374
1375     if (tree_is_expanded[fi->tree_type])
1376         gtk_tree_view_expand_to_path(tree_view, path);
1377     else
1378         gtk_tree_view_collapse_row(tree_view, path);
1379     return FALSE;
1380 }
1381
1382 void 
1383 proto_tree_draw_resolve(proto_tree *protocol_tree, GtkWidget *tree_view, const e_addr_resolve *resolv)
1384 {
1385     ProtoTreeModel *model;
1386
1387     model = proto_tree_model_new(protocol_tree, prefs.display_hidden_proto_items);
1388     if (resolv)
1389         proto_tree_model_force_resolv(PROTO_TREE_MODEL(model), resolv);
1390     gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(model));
1391
1392     gtk_tree_model_foreach(GTK_TREE_MODEL(model), expand_finfos, GTK_TREE_VIEW(tree_view));
1393     g_object_unref(G_OBJECT(model));
1394 }
1395
1396 /* fill the whole protocol tree with the string values */
1397 void
1398 proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
1399 {
1400     proto_tree_draw_resolve(protocol_tree, tree_view, NULL);
1401 }
1402
1403 void
1404 select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
1405 {
1406     if (recent.gui_bytes_view != view) {
1407         recent.gui_bytes_view = view;
1408         redraw_packet_bytes_all();
1409     }
1410 }