Fix the wireless settings button for AirPCap devices in the
[obnox/wireshark/wip.git] / gtk / packet_win.c
1 /* packet_win.c
2  * Routines for popping a window to display current packet
3  *
4  * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  * To do:
27  * - Add close button to bottom.
28  * - improve the window Title and allow user to config it
29  * - Add print support ? ( could be a mess)
30  * - Add button to have main window jump to this packet ?
31  */
32
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #ifdef HAVE_SYS_TYPES_H
39 #include <sys/types.h>
40 #endif
41
42 #include <gtk/gtk.h>
43 #include <gdk/gdkkeysyms.h>
44 #if GTK_CHECK_VERSION(3,0,0)
45 # include <gdk/gdkkeysyms-compat.h>
46 #endif
47
48 #include <string.h>
49
50 #include <epan/epan.h>
51 #include <epan/timestamp.h>
52 #include <epan/packet.h>
53 #include <epan/prefs.h>
54 #include <epan/column.h>
55 #include <epan/addr_resolv.h>
56 #include <epan/plugins.h>
57 #include <epan/epan_dissect.h>
58 #include <epan/strutil.h>
59 #include <epan/tvbuff-int.h>
60
61 #include "../file.h"
62 #include "../print.h"
63 #include "../ui_util.h"
64 #include "../summary.h"
65 #include "../simple_dialog.h"
66
67 #include "gtk/font_utils.h"
68 #include "gtk/main.h"
69 #include "gtk/packet_win.h"
70 #include "gtk/main_proto_draw.h"
71 #include "gtk/keys.h"
72 #include "gtk/gtkglobals.h"
73 #include "gtk/gui_utils.h"
74 #include "gtk/recent.h"
75
76 #define BV_SIZE 75
77 #define TV_SIZE 95
78
79 /* Data structure holding information about a packet-detail window. */
80 struct PacketWinData {
81         frame_data *frame;         /* The frame being displayed */
82         union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
83         guint8     *pd;            /* Data for packet */
84         GtkWidget  *main;
85         GtkWidget  *tv_scrollw;
86         GtkWidget  *tree_view;
87         GtkWidget  *bv_nb_ptr;
88         field_info *finfo_selected;
89         epan_dissect_t  edt;
90
91         int pd_offset;
92         int pd_bitoffset;
93 };
94
95 #ifdef WANT_PACKET_EDITOR
96 struct FieldinfoWinData {
97         frame_data *frame;         /* The frame being displayed */
98         union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
99         guint8     *pd;            /* Data for packet */
100         int start_offset;
101
102         field_info *finfo;
103 /* fvalue */
104         GtkWidget *edit;
105         GtkWidget *repr;
106 /* byteviews */
107         GtkWidget *bv;
108         GtkWidget *app_bv;
109
110         int pd_offset;
111         int pd_bitoffset;
112 };
113
114 struct CommonWinData {
115         frame_data *frame;         /* The frame being displayed */
116         guint8     *pd;            /* Data for packet */
117
118         int pd_offset;
119         int pd_bitoffset;
120         int val;
121 };
122
123 static gboolean edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr);
124 #endif
125
126 /* List of all the packet-detail windows popped up. */
127 static GList *detail_windows;
128
129 static void new_tree_view_selection_changed_cb(GtkTreeSelection *sel,
130                                                gpointer user_data);
131
132
133 static void destroy_new_window(GObject *object, gpointer user_data);
134
135 static gboolean
136 button_press_handler(GtkWidget *widget, GdkEvent *event, gpointer data _U_)
137 {
138         if (widget == NULL || event == NULL) {
139                 return FALSE;
140         }
141
142         tree_view_select(widget, (GdkEventButton *) event);
143
144         /* GDK_2BUTTON_PRESS is a doubleclick -> expand/collapse tree row */
145         if (event->type == GDK_2BUTTON_PRESS) {
146                 GtkTreePath      *path;
147
148                 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
149                                                   (gint) (((GdkEventButton *)event)->x),
150                                                   (gint) (((GdkEventButton *)event)->y),
151                                                   &path, NULL, NULL, NULL))
152                 {
153                         if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path)) {
154                                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
155                         }       else {
156                                 gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE);
157                         }
158                         gtk_tree_path_free(path);
159                 }
160         }
161
162         return FALSE;
163 }
164
165 #ifdef WANT_PACKET_EDITOR
166 static field_info *
167 proto_finfo_find(proto_tree *tree, field_info *old_finfo)
168 {
169         proto_node *node;
170
171         for (node = tree->first_child; node != NULL; node = node->next) {
172                 field_info *cur = PNODE_FINFO(node);
173
174                 if (!cur)
175                         continue;
176
177                 /* check everything, if it doesn't work report to me */
178                 if (cur->hfinfo == old_finfo->hfinfo &&
179                         cur->start == old_finfo->start && cur->length == old_finfo->length &&
180                         cur->appendix_start == old_finfo->appendix_start && cur->appendix_length == old_finfo->appendix_length &&
181                         cur->tree_type == old_finfo->tree_type && cur->flags == old_finfo->flags)
182                 {
183                         return cur;
184                 }
185
186                 if ((cur = proto_finfo_find((proto_tree *)node, old_finfo)))
187                         return cur;
188         }
189         return NULL;
190 }
191
192 static gboolean
193 finfo_window_refresh(struct FieldinfoWinData *DataPtr)
194 {
195         field_info *old_finfo = DataPtr->finfo;
196         field_info *finfo;
197         epan_dissect_t edt;
198
199         const guint8 *data;
200         GtkWidget *byte_view;
201         gchar label_str[ITEM_LABEL_LENGTH];
202
203         /* always update byteviews */
204         if (DataPtr->bv && (byte_view = get_notebook_bv_ptr(DataPtr->bv))) {
205                 int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->start;
206
207                 if (pos_inside < 0 || pos_inside >= old_finfo->length)
208                         pos_inside = -1;
209
210                 data = DataPtr->pd + DataPtr->start_offset + old_finfo->start;
211                 packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->length);
212         }
213
214         if (DataPtr->app_bv && (byte_view = get_notebook_bv_ptr(DataPtr->app_bv))) {
215                 int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->appendix_start;
216
217                 if (pos_inside < 0 || pos_inside >= old_finfo->appendix_length)
218                         pos_inside = -1;
219
220                 data = DataPtr->pd + DataPtr->start_offset + old_finfo->appendix_start;
221                 packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->appendix_length);
222         }
223
224         /* redisect */
225         epan_dissect_init(&edt, TRUE, TRUE);
226         /* Makes any sense?
227         if (old_finfo->hfinfo)
228                 proto_tree_prime_hfid(edt.tree, old_finfo->hfinfo->id);
229         */
230         epan_dissect_run(&edt, &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
231
232         /* Try to find finfo which looks like old_finfo.
233          * We might not found one, if protocol requires specific magic values, etc... */
234         if (!(finfo = proto_finfo_find(edt.tree, old_finfo))) {
235                 epan_dissect_cleanup(&edt);
236                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), "[finfo not found, try with another value, or restore old. If you think it is bug, fill bugreport]");
237                 return FALSE;
238         }
239
240         /* XXX, update fvalue_edit, e.g. when hexedit was changed */
241
242         if (finfo->rep == NULL) {
243                 proto_item_fill_label(finfo, label_str);
244                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), label_str);
245         } else
246                 gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), finfo->rep->representation);
247
248         epan_dissect_cleanup(&edt);
249         return TRUE;
250 }
251
252 static void
253 finfo_integer_common(struct FieldinfoWinData *DataPtr, guint64 u_val)
254 {
255         const field_info *finfo = DataPtr->finfo;
256         const header_field_info *hfinfo = finfo->hfinfo;
257         /* XXX, appendix? */
258         unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
259         int finfo_length = finfo->length;
260
261         if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
262                 guint32 u_mask = hfinfo->bitmask;
263
264                 while (finfo_length--) {
265                         guint8 *ptr = (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN)) ?
266                                         &(DataPtr->pd[finfo_offset++]) :
267                                         &(DataPtr->pd[finfo_offset + finfo_length]);
268
269                         if (u_mask) {
270                                 guint8 n_val = *ptr;
271                                 int i;
272
273                                 for (i = 0; i < 8; i++) {
274                                         if (u_mask & 1) {
275                                                 if (u_val & 1)
276                                                         n_val |= (1 << i);
277                                                 else
278                                                         n_val &= ~(1 << i);
279                                         }
280                                         u_mask >>= 1;
281                                         u_val >>= 1;
282                                 }
283                                 *ptr = n_val;
284
285                                 if (!u_mask)
286                                         break;
287                         } else {
288                                 *ptr = u_val & 0xff;
289                                 u_val >>= 8;
290                         }
291                 }
292         }
293         finfo_window_refresh(DataPtr);
294 }
295
296 static void
297 finfo_string_changed(GtkEditable *editable, gpointer user_data)
298 {
299         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
300
301         /* XXX, appendix? */
302         const field_info *finfo = DataPtr->finfo;
303         unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
304         int finfo_length = finfo->length;
305         int finfo_type = (finfo->hfinfo) ? finfo->hfinfo->type : FT_NONE;
306
307         const gchar *val = gtk_entry_get_text(GTK_ENTRY(editable));
308
309         if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
310                 /* strncpy */
311                 while (finfo_length && *val) {
312                         DataPtr->pd[finfo_offset++] = *val;
313                         finfo_length--;
314                         val++;
315                 }
316
317                 /* When FT_STRINGZ is there free space for NUL? */
318                 if (finfo_type == FT_STRINGZ && finfo_length) {
319                         DataPtr->pd[finfo_offset++] = '\0';
320                         finfo_length--;
321                 }
322
323                 /* XXX, string shorter than previous one. Warn user (red background?), for now fill with NULs */
324                 while (finfo_length > 0) {
325                         DataPtr->pd[finfo_offset++] = '\0';
326                         finfo_length--;
327                 }
328         }
329         finfo_window_refresh(DataPtr);
330 }
331
332 static void
333 finfo_boolean_changed(GtkToggleButton *togglebutton, gpointer user_data)
334 {
335         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
336
337         gboolean val = gtk_toggle_button_get_active(togglebutton);
338
339         finfo_integer_common(DataPtr, val ? G_MAXUINT64 : 0);
340 }
341
342 static void
343 finfo_integer_changed(GtkSpinButton *spinbutton, gpointer user_data)
344 {
345         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
346
347         const field_info *finfo = DataPtr->finfo;
348         const header_field_info *hfinfo = finfo->hfinfo;
349         int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
350
351         gdouble val = gtk_spin_button_get_value(spinbutton);
352         guint64 u_val;
353
354         if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 || finfo_type == FT_INT64)
355                 u_val = (guint64) ((gint64) val);
356
357         else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 || finfo_type == FT_UINT64)
358                 u_val = (guint64) val;
359         else {
360                 g_assert_not_reached();
361                 return;
362         }
363
364         if (hfinfo->bitmask && hfinfo->bitshift > 0)
365                 u_val <<= hfinfo->bitshift;
366
367         finfo_integer_common(DataPtr, u_val);
368 }
369
370 static void
371 finfo_ipv4_changed(GtkSpinButton *spinbutton, gpointer user_data)
372 {
373         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
374
375         gdouble val = gtk_spin_button_get_value(spinbutton);
376
377         finfo_integer_common(DataPtr, (guint32) val);
378 }
379
380 static gboolean
381 finfo_bv_key_pressed_cb(GtkWidget *bv _U_, GdkEventKey *event, gpointer user_data)
382 {
383         struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *)user_data;
384         const field_info *finfo = DataPtr->finfo;
385         struct CommonWinData data;
386         gboolean have_appendix;
387         gboolean ret;
388
389         /* save */
390         data.frame = DataPtr->frame;
391         data.pd = DataPtr->pd;
392         data.pd_offset = DataPtr->pd_offset;
393         data.pd_bitoffset = DataPtr->pd_bitoffset;
394
395         ret = edit_pkt_common_key_pressed_cb(event, &data);
396
397         /* restore */
398         DataPtr->pd_offset = data.pd_offset;
399         DataPtr->pd_bitoffset = data.pd_bitoffset;
400
401         /* XXX, assuming finfo->appendix_start >= finfo->start, and if appendix exists, main exists also.
402          *      easy to fix if needed */
403         have_appendix = (finfo->appendix_start >= 0 && finfo->appendix_length > 0);
404
405         if ((DataPtr->pd_offset >= DataPtr->start_offset + finfo->start && DataPtr->pd_offset < DataPtr->start_offset + finfo->start + finfo->length) ||
406                 (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length))
407                 { /* pd_offset ok */ }
408         else
409         if (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length) {
410                 DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
411                 DataPtr->pd_bitoffset = 0; /* first bit */
412
413         } else if (DataPtr->pd_offset >= DataPtr->start_offset + finfo->start + finfo->length) {
414                 if (have_appendix)
415                         DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start;
416                 else
417                         DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
418                 DataPtr->pd_bitoffset = 0; /* first bit */
419         }
420         else
421         if (DataPtr->pd_offset < DataPtr->start_offset + finfo->start) {
422                 if (have_appendix)
423                         DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length-1;
424                 else
425                         DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
426                 /* XXX, last bit/octect? */
427
428         } else if (have_appendix && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start) {
429                 DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
430                 /* XXX, last bit/octect? */
431         }
432
433         if (ret)
434                 finfo_window_refresh(DataPtr);
435         return ret;
436 }
437
438 static gint
439 finfo_ipv4_input(GtkSpinButton *spinbutton, gpointer arg1, gpointer user_data _U_)
440 {
441         const gchar *addr_str = gtk_entry_get_text(GTK_ENTRY(spinbutton));
442         gdouble *out_val = (gdouble *) arg1;
443         guint32 addr;
444 #if 0
445         /* XXX, get_host_ipaddr() support hostname resolution */
446         if (!get_host_ipaddr(addr_str, &addr))
447                 return GTK_INPUT_ERROR;
448         addr = GUINT32_FROM_BE(addr);
449 #else
450         unsigned int a0, a1, a2, a3;
451
452         if (sscanf(addr_str, "%u.%u.%u.%u", &a0, &a1, &a2, &a3) != 4)
453                 return GTK_INPUT_ERROR;
454
455         if (a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
456                 return GTK_INPUT_ERROR;
457
458         addr = a0 << 24 | a1 << 16 | a2 << 8 | a3;
459 #endif
460         *out_val = (gdouble) addr;
461         return TRUE;
462 }
463
464 static gboolean
465 finfo_ipv4_output(GtkSpinButton *spinbutton, gpointer user_data _U_)
466 {
467         GtkAdjustment *adj;
468         guint32 value;
469
470         adj = gtk_spin_button_get_adjustment(spinbutton);
471         value = (guint32) gtk_adjustment_get_value(adj);
472         value = GUINT32_TO_BE(value);
473         /* ip_to_str_buf((guint8*)&value, buf, MAX_IP_STR_LEN); */      /* not exported */
474         gtk_entry_set_text(GTK_ENTRY(spinbutton), ip_to_str((guint8*)&value));  /* XXX, can we ep_alloc() inside gui? */
475         return TRUE;
476 }
477
478 static gint
479 new_finfo_window(GtkWidget *w, struct FieldinfoWinData *DataPtr)
480 {
481         field_info *finfo = DataPtr->finfo;
482         const header_field_info *hfinfo = finfo->hfinfo;
483         int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
484
485         GtkWidget *dialog = gtk_dialog_new_with_buttons("Editing finfo: ....",
486                         GTK_WINDOW(w),
487                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
488                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
489                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
490                         NULL);
491
492         GtkWidget *dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
493         GtkWidget *fvalue_edit;
494         GtkWidget *native_repr;
495         GtkWidget *bv_nb_ptr;
496         GtkWidget *frame, *frame_vbox;
497
498         gint result;
499
500         if (!FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN) && !FI_GET_FLAG(finfo, FI_BIG_ENDIAN)) {
501                 fvalue_edit = gtk_entry_new();
502                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not added by proto_tree_add_item()>");
503                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
504                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
505
506         } /* else if (XXX) {
507                 fvalue_edit = gtk_entry_new();
508                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<ERROR: Value stored in finfo doesn't match value from tvb>");
509                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
510                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
511
512         } */ else if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 ||
513                         finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32)
514         {
515 #if GTK_CHECK_VERSION(3,0,0)
516                 GtkAdjustment *adj;
517 #else
518                 GtkObject *adj;
519 #endif
520                 int bitcount = 0;
521
522                 if (finfo_type == FT_INT8 || finfo_type == FT_UINT8)
523                         bitcount = 8;
524                 if (finfo_type == FT_INT16 || finfo_type == FT_UINT16)
525                         bitcount = 16;
526                 if (finfo_type == FT_INT24 || finfo_type == FT_UINT24)
527                         bitcount = 24;
528                 if (finfo_type == FT_INT32 || finfo_type == FT_UINT32)
529                         bitcount = 32;
530                 /* if (finfo_type == FT_INT64 || finfo_type == FT_UINT64)
531                         bitcount = 64; */
532
533                 if (finfo->length * 8 < bitcount)
534                         bitcount = finfo->length / 8;
535
536                 if (hfinfo->bitmask && hfinfo->bitshift > 0)
537                         bitcount -= hfinfo->bitshift;
538
539                 /* XXX, hfinfo->bitmask: Can we configure GTK_ADJUSTMENT to do custom step? (value-changed signal?) */
540
541                 /* XXX, I'm little worried about these casts from (unsigned) integer to double... */
542
543                 if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 /* || finfo_type == FT_INT64 */)
544                         adj = gtk_adjustment_new((double) fvalue_get_sinteger(&finfo->value), (double) -(G_GINT64_CONSTANT(1) << (bitcount-1)), (double) ((G_GINT64_CONSTANT(1) << (bitcount-1))-1), 1.0, 10.0, 0);
545                 else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 /* || finfo_type == FT_UINT64 */ )
546                         adj = gtk_adjustment_new((double) fvalue_get_uinteger(&finfo->value), 0.0, (double) ((G_GINT64_CONSTANT(1U) << bitcount)-1), 1.0, 10.0, 0);
547                 else {
548                         g_assert_not_reached();
549                         goto not_supported;
550                 }
551
552                 fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
553                 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(fvalue_edit), TRUE);
554                 g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_integer_changed), DataPtr);
555
556         } else if (finfo_type == FT_STRING || finfo_type == FT_STRINGZ) {
557                 fvalue_edit = gtk_entry_new();
558                 gtk_entry_set_max_length(GTK_ENTRY(fvalue_edit), finfo->length);
559                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), fvalue_get(&finfo->value));
560                 g_signal_connect(fvalue_edit, "changed", G_CALLBACK(finfo_string_changed), DataPtr);
561
562         } else if (finfo_type == FT_BOOLEAN) {
563                 fvalue_edit = gtk_check_button_new();
564                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fvalue_edit), (fvalue_get_uinteger(&finfo->value) != 0));
565                 g_signal_connect(fvalue_edit, "toggled", G_CALLBACK(finfo_boolean_changed), DataPtr);
566
567         } else if (finfo_type == FT_IPv4) {
568                 guint32 net_addr = ipv4_get_net_order_addr(fvalue_get(&finfo->value));
569 #if GTK_CHECK_VERSION(3,0,0)
570                 GtkAdjustment *adj;
571 #else
572                 GtkObject *adj;
573 #endif
574                 adj = gtk_adjustment_new((double) (GUINT32_FROM_BE(net_addr)), 0.0, 4294967295.0 /* (2^32)-1 */, 1.0, 256.0, 0);
575
576                 /* XXX, create four gtk_spin_button_new which takes 0..255 */
577                 fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
578                 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(fvalue_edit), GTK_UPDATE_IF_VALID);
579                 g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_ipv4_changed), DataPtr);
580                 g_signal_connect(fvalue_edit, "input", G_CALLBACK(finfo_ipv4_input), NULL);
581                 g_signal_connect(fvalue_edit, "output", G_CALLBACK(finfo_ipv4_output), NULL);
582
583         } else {
584 not_supported:
585                 /* List of unsupported FT_*:
586                         FT_NONE, FT_PROTOCOL,
587                         FT_BYTES, FT_UINT_BYTES,
588                         FT_INT64, FT_UINT64,                    ; should work with FT_INT[8,16,24,32] code
589                         FT_FLOAT, FT_DOUBLE,
590                         FT_IPXNET, FT_IPv6, FT_ETHER,
591                         FT_GUID, FT_OID,
592                         FT_UINT_STRING,
593                         FT_ABSOLUTE_TIME, FT_RELATIVE_TIME
594                 */
595                 fvalue_edit = gtk_entry_new();
596                 gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not supported>");
597                 gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
598                 gtk_widget_set_sensitive(fvalue_edit, FALSE);
599         }
600         gtk_box_pack_start(GTK_BOX(dialog_vbox), fvalue_edit, FALSE, FALSE, 0);
601         gtk_widget_show(fvalue_edit);
602
603         DataPtr->edit = fvalue_edit;
604
605         native_repr = gtk_entry_new();
606         gtk_editable_set_editable(GTK_EDITABLE(native_repr), FALSE);
607         gtk_widget_set_sensitive(native_repr, FALSE);
608         gtk_box_pack_start(GTK_BOX(dialog_vbox), native_repr, FALSE, FALSE, 0);
609         gtk_widget_show(native_repr);
610
611         DataPtr->repr = native_repr;
612
613         frame = gtk_frame_new("Hex edit");
614         frame_vbox = gtk_vbox_new(TRUE, 1);
615
616         /* raw hex edit */
617         if (finfo->start >= 0 && finfo->length > 0) {
618                 GtkWidget *byte_view;
619                 /* Byte view */
620                 bv_nb_ptr = byte_view_new();
621                 gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
622                 gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
623                 gtk_widget_show(bv_nb_ptr);
624
625                 if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
626                         g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
627                 DataPtr->bv = bv_nb_ptr;
628         }
629
630         if (finfo->appendix_start >= 0 && finfo->appendix_length > 0) {
631                 GtkWidget *byte_view;
632                 /* Appendix byte view */
633                 bv_nb_ptr = byte_view_new();
634                 gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
635                 gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
636                 gtk_widget_show(bv_nb_ptr);
637
638                 if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
639                         g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
640                 DataPtr->app_bv = bv_nb_ptr;
641         }
642         gtk_container_add(GTK_CONTAINER(frame), frame_vbox);
643         gtk_widget_show(frame_vbox); gtk_widget_show(frame);
644         gtk_container_add(GTK_CONTAINER(dialog_vbox), frame);
645
646         gtk_window_set_default_size(GTK_WINDOW(dialog), DEF_WIDTH, -1);
647         finfo_window_refresh(DataPtr);
648         result = gtk_dialog_run(GTK_DIALOG(dialog));
649         gtk_widget_destroy(dialog);
650         return result;
651 }
652
653 static void
654 edit_pkt_tree_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer user_data)
655 {
656         struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
657         GtkTreeModel *model;
658         GtkTreeIter iter;
659         field_info *finfo;
660
661         model = gtk_tree_view_get_model(tree_view);
662         if (!gtk_tree_model_get_iter(model, &iter, path))
663                 return;
664
665         gtk_tree_model_get(model, &iter, 1, &finfo, -1);
666         if (!finfo)
667                 return;
668
669         if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
670                         finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
671         {
672                 struct FieldinfoWinData data;
673
674                 data.frame = DataPtr->frame;
675                 data.pseudo_header = DataPtr->pseudo_header;
676                 data.pd = g_memdup(DataPtr->pd, DataPtr->frame->cap_len);
677                 data.start_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
678
679                 data.finfo = finfo;
680                 data.app_bv = data.bv = NULL;
681                 data.repr = data.edit = NULL;
682
683                 data.pd_offset = data.start_offset + finfo->start;
684                 data.pd_bitoffset = 0;
685
686                 if (new_finfo_window(DataPtr->main, &data) == GTK_RESPONSE_ACCEPT) {
687                         /* DataPtr->pseudo_header = data.pseudo_header; */
688                         memcpy(DataPtr->pd, data.pd, DataPtr->frame->cap_len);
689
690                         epan_dissect_cleanup(&(DataPtr->edt));
691                         epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
692                         epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
693                         add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
694                         proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
695                 }
696                 g_free(data.pd);
697
698         } else {
699                 /* XXX, simple_dialog() is shown on top of main_window, instead of edit_window. */
700                 simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "Item can't be edited. FI_GENERATED or tvb not subset of packet data (uncompressed?)");
701         }
702 }
703
704 static gboolean
705 edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr)
706 {
707         int val = -1;
708
709         switch (recent.gui_bytes_view) {
710         case BYTES_HEX:
711                 if (event->keyval >= 'a' && event->keyval <= 'f')
712                         val = (event->keyval - 'a') + 10;
713                 else if (event->keyval >= 'A' && event->keyval <= 'F')
714                         val = (event->keyval - 'A') + 10;
715                 else if (event->keyval >= '0' && event->keyval <= '9')
716                         val = (event->keyval - '0');
717                 else if (event->keyval == GDK_Left)
718                         DataPtr->pd_bitoffset -= 4;
719                 else if (event->keyval == GDK_Right)
720                         DataPtr->pd_bitoffset += 4;
721                 else
722                         return FALSE;
723
724                 if (val != -1) {
725                         /* Lazy...
726                          * XXX Allow (DataPtr->pd_bitoffset % 4) != 0 ? */
727                         if (DataPtr->pd_bitoffset < 4) {
728                                 DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0x0f) | (val << 4);
729                                 DataPtr->pd_bitoffset = 4;
730                         } else {
731                                 DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0xf0) | val;
732                                 DataPtr->pd_bitoffset = 8;
733                         }
734                         /* DataPtr->pd_bitoffset += 4; */
735                 }
736                 break;
737
738         case BYTES_BITS:
739                 if (event->keyval == '0' || event->keyval == '1')
740                         val = (event->keyval != '0');
741                 else if (event->keyval == GDK_Left)
742                         DataPtr->pd_bitoffset -= 1;
743                 else if (event->keyval == GDK_Right)
744                         DataPtr->pd_bitoffset += 1;
745                 else
746                         return FALSE;
747
748                 if (val != -1) {
749                         if (val)
750                                 DataPtr->pd[DataPtr->pd_offset] |= (1 << (7-DataPtr->pd_bitoffset));
751                         else
752                                 DataPtr->pd[DataPtr->pd_offset] &= ~(1 << (7-DataPtr->pd_bitoffset));
753                         DataPtr->pd_bitoffset += 1;
754                 }
755                 break;
756
757         default:
758                 g_assert_not_reached();
759                 return FALSE;
760         }
761
762         while (DataPtr->pd_bitoffset >= 8) {
763                 DataPtr->pd_offset += 1;
764                 DataPtr->pd_bitoffset -= 8;
765         }
766         while (DataPtr->pd_bitoffset < 0) {
767                 DataPtr->pd_offset -= 1;
768                 DataPtr->pd_bitoffset += 8;
769         }
770
771         DataPtr->val = val;
772         return TRUE;
773 }
774
775 static gboolean
776 edit_pkt_win_key_pressed_cb(GtkWidget *win _U_, GdkEventKey *event, gpointer user_data)
777 {
778         struct PacketWinData *DataPtr = (struct PacketWinData *)user_data;
779         struct CommonWinData data;
780         GSList *src_le;
781         gboolean ret;
782         tvbuff_t *ds_tvb = NULL;
783
784         /* save */
785         data.frame = DataPtr->frame;
786         data.pd = DataPtr->pd;
787         data.pd_offset = DataPtr->pd_offset;
788         data.pd_bitoffset = DataPtr->pd_bitoffset;
789
790         ret = edit_pkt_common_key_pressed_cb(event, &data);
791
792         /* restore */
793         DataPtr->pd_offset = data.pd_offset;
794         DataPtr->pd_bitoffset = data.pd_bitoffset;
795
796         if (DataPtr->pd_offset < 0) {
797                 DataPtr->pd_offset = DataPtr->frame->cap_len-1;
798                 /* XXX, last bit/octect? */
799         }
800
801         if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len) {
802                 DataPtr->pd_offset = 0;
803                 DataPtr->pd_bitoffset = 0; /* first bit */
804         }
805
806         if (!ret)
807                 return FALSE;
808
809         /* redissect if changed */
810         if (data.val != -1) {
811                 /* XXX, can be optimized? */
812                 epan_dissect_cleanup(&(DataPtr->edt));
813                 epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
814                 epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
815                 add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
816                 proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
817         }
818
819         for (src_le = DataPtr->edt.pi.data_src; src_le != NULL; src_le = src_le->next) {
820                 const data_source *src = src_le->data;
821                 tvbuff_t *tvb = src->tvb;
822
823                 if (tvb && tvb->real_data == DataPtr->pd) {
824                         ds_tvb = tvb;
825                         break;
826                 }
827         }
828
829         if (ds_tvb != NULL) {
830                 GtkWidget    *byte_view;
831
832                 set_notebook_page(DataPtr->bv_nb_ptr, ds_tvb);
833                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
834                 if (byte_view)
835                         packet_hex_editor_print(byte_view, DataPtr->pd, DataPtr->frame, DataPtr->pd_offset, DataPtr->pd_bitoffset, DataPtr->frame->cap_len);
836         }
837         return TRUE;
838 }
839
840 static void
841 edit_pkt_destroy_new_window(GObject *object _U_, gpointer user_data)
842 {
843         /* like destroy_new_window, but without freeding DataPtr->pd */
844         struct PacketWinData *DataPtr = user_data;
845
846         detail_windows = g_list_remove(detail_windows, DataPtr);
847         epan_dissect_cleanup(&(DataPtr->edt));
848         g_free(DataPtr);
849
850         /* XXX, notify main packet list that packet should be redisplayed */
851 }
852
853 static gint g_direct_compare_func(gconstpointer a, gconstpointer b, gpointer user_data _U_) {
854         if (a > b)
855                 return 1;
856         else if (a < b)
857                 return -1;
858         else
859                 return 0;
860 }
861
862 static void modifed_frame_data_free(gpointer data) {
863         modified_frame_data *mfd = (modified_frame_data *) data;
864
865         g_free(mfd->pd);
866         g_free(mfd);
867 }
868
869 #endif /* WANT_PACKET_EDITOR */
870
871 void new_packet_window(GtkWidget *w _U_, gboolean editable _U_)
872 {
873 #define NewWinTitleLen 1000
874         char Title[NewWinTitleLen] = "";
875         const char *TextPtr;
876         GtkWidget *main_w, *main_vbox, *pane,
877                 *tree_view, *tv_scrollw,
878                 *bv_nb_ptr;
879         struct PacketWinData *DataPtr;
880         int i;
881
882         if (!cfile.current_frame) {
883                 /* nothing has been captured so far */
884                 return;
885         }
886
887         /* With the new packetlists "lazy columns" it's neccesary to reread the frame */
888         if (!cf_read_frame(&cfile, cfile.current_frame)) {
889                 /* error reading the frame */
890                 return;
891         }
892
893         /* Allocate data structure to represent this window. */
894         DataPtr = (struct PacketWinData *) g_malloc(sizeof(struct PacketWinData));
895
896         DataPtr->frame = cfile.current_frame;
897         memcpy(&DataPtr->pseudo_header, &cfile.pseudo_header, sizeof DataPtr->pseudo_header);
898         DataPtr->pd = g_malloc(DataPtr->frame->cap_len);
899         memcpy(DataPtr->pd, cfile.pd, DataPtr->frame->cap_len);
900
901         epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
902         epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd,
903                          DataPtr->frame, &cfile.cinfo);
904         epan_dissect_fill_in_columns(&(DataPtr->edt), FALSE, TRUE);
905
906         /*
907          * Build title of window by getting column data constructed when the
908          * frame was dissected.
909          */
910         for (i = 0; i < cfile.cinfo.num_cols; ++i) {
911                 TextPtr = cfile.cinfo.col_data[i];
912                 if ((strlen(Title) + strlen(TextPtr)) < NewWinTitleLen - 1) {
913                         g_strlcat(Title, TextPtr, NewWinTitleLen);
914                         g_strlcat(Title, " ", NewWinTitleLen);
915                 }
916         }
917
918         main_w = window_new(GTK_WINDOW_TOPLEVEL, Title);
919         gtk_window_set_default_size(GTK_WINDOW(main_w), DEF_WIDTH, -1);
920
921         /* Container for paned windows  */
922         main_vbox = gtk_vbox_new(FALSE, 1);
923         gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 1);
924         gtk_container_add(GTK_CONTAINER(main_w), main_vbox);
925         gtk_widget_show(main_vbox);
926
927         /* Panes for the tree and byte view */
928         pane = gtk_vpaned_new();
929         gtk_container_add(GTK_CONTAINER(main_vbox), pane);
930         gtk_widget_show(pane);
931
932         /* Tree view */
933         tv_scrollw = main_tree_view_new(&prefs, &tree_view);
934         gtk_paned_pack1(GTK_PANED(pane), tv_scrollw, TRUE, TRUE);
935         gtk_widget_set_size_request(tv_scrollw, -1, TV_SIZE);
936         gtk_widget_show(tv_scrollw);
937         gtk_widget_show(tree_view);
938
939         /* Byte view */
940         bv_nb_ptr = byte_view_new();
941         gtk_paned_pack2(GTK_PANED(pane), bv_nb_ptr, FALSE, FALSE);
942         gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
943         gtk_widget_show(bv_nb_ptr);
944
945         DataPtr->main = main_w;
946         DataPtr->tv_scrollw = tv_scrollw;
947         DataPtr->tree_view = tree_view;
948         DataPtr->bv_nb_ptr = bv_nb_ptr;
949         detail_windows = g_list_append(detail_windows, DataPtr);
950
951         /* load callback handlers */
952         g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
953                          "changed", G_CALLBACK(new_tree_view_selection_changed_cb), DataPtr);
954         g_signal_connect(tree_view, "button_press_event", G_CALLBACK(button_press_handler), NULL);
955 #ifdef WANT_PACKET_EDITOR
956         if (editable && DataPtr->frame->cap_len != 0) {
957                 g_signal_connect(main_w, "key-press-event", G_CALLBACK(edit_pkt_win_key_pressed_cb), DataPtr);
958                 /* XXX, popup-menu instead of row-activated? */
959                 g_signal_connect(tree_view, "row-activated", G_CALLBACK(edit_pkt_tree_row_activated_cb), DataPtr);
960                 g_signal_connect(main_w, "destroy", G_CALLBACK(edit_pkt_destroy_new_window), DataPtr);
961         } else
962 #endif
963                 g_signal_connect(main_w, "destroy", G_CALLBACK(destroy_new_window), DataPtr);
964
965         /* draw the protocol tree & print hex data */
966         add_byte_views(&(DataPtr->edt), tree_view, DataPtr->bv_nb_ptr);
967         proto_tree_draw(DataPtr->edt.tree, tree_view);
968
969         DataPtr->finfo_selected = NULL;
970         DataPtr->pd_offset = 0;
971         DataPtr->pd_bitoffset = 0;
972         gtk_widget_show(main_w);
973
974 #ifdef WANT_PACKET_EDITOR
975         if (editable && DataPtr->frame->cap_len != 0) {
976                 /* XXX, there's no Save button here, so lets assume packet is always edited */
977                 modified_frame_data *mfd = g_malloc(sizeof(modified_frame_data));
978
979                 mfd->pd = DataPtr->pd;
980                 mfd->ph = DataPtr->pseudo_header;
981
982                 if (cfile.edited_frames == NULL)
983                         cfile.edited_frames = g_tree_new_full(g_direct_compare_func, NULL, NULL, modifed_frame_data_free);
984                 g_tree_insert(cfile.edited_frames, GINT_TO_POINTER(DataPtr->frame->num), mfd);
985                 DataPtr->frame->file_off = -1;
986         }
987 #endif
988 }
989
990 static void
991 destroy_new_window(GObject *object _U_, gpointer user_data)
992 {
993         struct PacketWinData *DataPtr = user_data;
994
995         detail_windows = g_list_remove(detail_windows, DataPtr);
996         epan_dissect_cleanup(&(DataPtr->edt));
997         g_free(DataPtr->pd);
998         g_free(DataPtr);
999 }
1000
1001 /* called when a tree row is (un)selected in the popup packet window */
1002 static void
1003 new_tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data)
1004 {
1005         field_info   *finfo;
1006         GtkWidget    *byte_view;
1007         const guint8 *data;
1008         guint         len;
1009         GtkTreeModel *model;
1010         GtkTreeIter   iter;
1011
1012         struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
1013
1014         /* if something is selected */
1015         if (gtk_tree_selection_get_selected(sel, &model, &iter))
1016         {
1017                 gtk_tree_model_get(model, &iter, 1, &finfo, -1);
1018                 if (!finfo) return;
1019
1020                 set_notebook_page(DataPtr->bv_nb_ptr, finfo->ds_tvb);
1021                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
1022                 if (!byte_view) /* exit if no hex window to write in */
1023                         return;
1024
1025                 data = get_byte_view_data_and_length(byte_view, &len);
1026                 if (data == NULL) {
1027                         data = DataPtr->pd;
1028                         len =  DataPtr->frame->cap_len;
1029                 }
1030
1031                 DataPtr->finfo_selected = finfo;
1032
1033 #ifdef WANT_PACKET_EDITOR
1034                 DataPtr->pd_offset = 0;
1035                 DataPtr->pd_bitoffset = 0;
1036
1037                 if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
1038                     finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
1039                 {
1040                         /* I haven't really test if TVB subsets works, but why not? :> */
1041                         int pd_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
1042
1043                         /* some code from packet_hex_print */
1044                         int finfo_offset = finfo->start;
1045                         int finfo_len = finfo->length;
1046
1047                         if (!(finfo_offset >= 0 && finfo_len > 0)) {
1048                                 finfo_offset = finfo->appendix_start;
1049                                 finfo_len = finfo->appendix_length;
1050                         }
1051
1052                         /* Don't care about things like bitmask or LE/BE, just point DataPtr->tvb_[bit]offset to proper offsets. */
1053                         if (finfo_offset >= 0 && finfo_len > 0) {
1054                                 DataPtr->pd_offset = pd_offset + finfo_offset;
1055                                 DataPtr->pd_bitoffset = 0; /* XXX */
1056                         }
1057
1058                         if (DataPtr->pd_offset < 0)
1059                                 DataPtr->pd_offset = 0;
1060                         if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len)
1061                                 DataPtr->pd_offset = 0;
1062                 }
1063 #endif
1064
1065                 packet_hex_print(byte_view, data, DataPtr->frame, finfo, len);
1066         }
1067         else
1068         {
1069                 DataPtr->finfo_selected = NULL;
1070
1071                 byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
1072                 if (!byte_view) /* exit if no hex window to write in */
1073                         return;
1074
1075                 data = get_byte_view_data_and_length(byte_view, &len);
1076                 g_assert(data != NULL);
1077                 packet_hex_reprint(byte_view);
1078         }
1079 }
1080
1081 /* Functions called from elsewhere to act on all popup packet windows. */
1082
1083 /* Destroy all popup packet windows. */
1084 void
1085 destroy_packet_wins(void)
1086 {
1087         struct PacketWinData *DataPtr;
1088
1089         /* Destroying a packet window causes it to be removed from
1090            the list of packet windows, so we can't do a "g_list_foreach()"
1091            to go through the list of all packet windows and destroy them
1092            as we find them; instead, as long as the list is non-empty,
1093            we destroy the first window on the list. */
1094         while (detail_windows != NULL) {
1095                 DataPtr = (struct PacketWinData *)(detail_windows->data);
1096                 window_destroy(DataPtr->main);
1097         }
1098 }
1099
1100 static void
1101 redraw_packet_bytes_cb(gpointer data, gpointer user_data _U_)
1102 {
1103         struct PacketWinData *DataPtr = (struct PacketWinData *)data;
1104
1105         redraw_packet_bytes(DataPtr->bv_nb_ptr, DataPtr->frame, DataPtr->finfo_selected);
1106 }
1107
1108 /* Redraw the packet bytes part of all the popup packet windows. */
1109 void
1110 redraw_packet_bytes_packet_wins(void)
1111 {
1112         g_list_foreach(detail_windows, redraw_packet_bytes_cb, NULL);
1113 }