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