From Alejandro Vaquero
[obnox/wireshark/wip.git] / gtk / menu.c
1 /* menu.c
2  * Menu routines
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30
31 #include <string.h>
32
33 #include "main.h"
34 #include "menu.h"
35 #include "tap_menu.h"
36 #include <epan/packet.h>
37 #include <epan/addr_resolv.h>
38 #include <epan/prefs.h>
39 #include <epan/tap.h>
40 #include <epan/timestamp.h>
41 #include <epan/ipproto.h>
42
43 #include "about_dlg.h"
44 #include "capture_dlg.h"
45 #include "color_dlg.h"
46 #include "filter_dlg.h"
47 #include "dlg_utils.h"
48 #include "file_dlg.h"
49 #include "find_dlg.h"
50 #include "goto_dlg.h"
51 #include "summary_dlg.h"
52 #include "prefs_dlg.h"
53 #include "packet_win.h"
54 #include "print.h"
55 #include "follow_dlg.h"
56 #include "decode_as_dlg.h"
57 #include "help_dlg.h"
58 #include "supported_protos_dlg.h"
59 #include "proto_dlg.h"
60 #include "proto_hier_stats_dlg.h"
61 #include "keys.h"
62 #include <epan/plugins.h>
63 #include <epan/epan_dissect.h>
64 #include "compat_macros.h"
65 #include "toolbar.h"
66 #include "gtkglobals.h"
67 #include "register.h"
68 #include "../menu.h"
69 #include "packet_list.h"
70 #include "ethclist.h"
71 #include "recent.h"
72 #include "../ui_util.h"
73 #include "proto_draw.h"
74 #include "conversations_table.h"
75 #include "hostlist_table.h"
76 #include "simple_dialog.h"
77 #include "packet_history.h"
78
79 GtkWidget *popup_menu_object;
80
81 static void
82 clear_menu_recent_capture_file_cmd_cb(GtkWidget *w, gpointer unused _U_);
83
84 typedef struct _menu_item {
85     char    *name;
86     gint    group;
87     gboolean enabled;
88     GtkItemFactoryCallback callback;
89     gpointer callback_data;
90     gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *);
91     gboolean (*selected_tree_row_enabled)(field_info *);
92     GList *children;
93 } menu_item_t;
94
95 static GList *tap_menu_tree_root = NULL;
96
97 static void 
98 merge_all_tap_menus(GList *node);
99
100 #define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
101
102 static void menus_init(void);
103 static void set_menu_sensitivity (GtkItemFactory *, const gchar *, gint);
104 static void main_toolbar_show_cb(GtkWidget *w _U_, gpointer d _U_);
105 static void filter_toolbar_show_cb(GtkWidget *w _U_, gpointer d _U_);
106 static void packet_list_show_cb(GtkWidget *w _U_, gpointer d _U_);
107 static void tree_view_show_cb(GtkWidget *w _U_, gpointer d _U_);
108 static void byte_view_show_cb(GtkWidget *w _U_, gpointer d _U_);
109 static void statusbar_show_cb(GtkWidget *w _U_, gpointer d _U_);
110 static void timestamp_absolute_cb(GtkWidget *w _U_, gpointer d _U_);
111 static void timestamp_absolute_date_cb(GtkWidget *w _U_, gpointer d _U_);
112 static void timestamp_relative_cb(GtkWidget *w _U_, gpointer d _U_);
113 static void timestamp_delta_cb(GtkWidget *w _U_, gpointer d _U_);
114 static void name_resolution_mac_cb(GtkWidget *w _U_, gpointer d _U_);
115 static void name_resolution_network_cb(GtkWidget *w _U_, gpointer d _U_);
116 static void name_resolution_transport_cb(GtkWidget *w _U_, gpointer d _U_);
117 #ifdef HAVE_LIBPCAP
118 static void auto_scroll_live_cb(GtkWidget *w _U_, gpointer d _U_);
119 #endif
120
121 /* This is the GtkItemFactoryEntry structure used to generate new menus.
122        Item 1: The menu path. The letter after the underscore indicates an
123                accelerator key once the menu is open.
124        Item 2: The accelerator key for the entry
125        Item 3: The callback function.
126        Item 4: The callback action.  This changes the parameters with
127                which the function is called.  The default is 0.
128        Item 5: The item type, used to define what kind of an item it is.
129                Here are the possible values:
130
131                NULL               -> "<Item>"
132                ""                 -> "<Item>"
133                "<Title>"          -> create a title item
134                "<Item>"           -> create a simple item
135                "<ImageItem>"      -> create an item holding an image (gtk2)
136                "<StockItem>"      -> create an item holding a stock image (gtk2)
137                "<CheckItem>"      -> create a check item
138                "<ToggleItem>"     -> create a toggle item
139                "<RadioItem>"      -> create a radio item
140                <path>             -> path of a radio item to link against
141                "<Separator>"      -> create a separator
142                "<Tearoff>"        -> create a tearoff separator (gtk2)
143                "<Branch>"         -> create an item to hold sub items (optional)
144                "<LastBranch>"     -> create a right justified branch
145        Item 6: extra data needed for ImageItem and StockItem (gtk2)
146     */
147
148 /*  As a general GUI guideline, we try to follow the Gnome Human Interface Guidelines, which can be found at:
149     http://developer.gnome.org/projects/gup/hig/1.0/index.html
150     
151 Please note: there are some differences between the Gnome HIG menu suggestions and our implementation:
152
153 File/Open Recent:   the Gnome HIG suggests putting the list of recently used files as elements into the File menuitem.
154                     As this is ok for only a few items, this will become unhandy for 10 or even more list entries.
155                     For this reason, we use a submenu for this.
156
157 File/Close:         the Gnome HIG suggests putting this item just above the Quit item.
158                     This results in unintuitive behaviour as both Close and Quit items are very near together.
159                     By putting the Close item near the open item(s), it better suggests that it will close the 
160                     currently opened/captured file only.
161 */
162
163
164 /* main menu */
165 static GtkItemFactoryEntry menu_items[] =
166 {
167     ITEM_FACTORY_ENTRY("/_File", NULL, NULL, 0, "<Branch>", NULL),
168     ITEM_FACTORY_STOCK_ENTRY("/File/_Open...", "<control>O", file_open_cmd_cb,
169                              0, GTK_STOCK_OPEN),
170     ITEM_FACTORY_ENTRY("/File/Open _Recent", NULL, NULL, 0, "<Branch>", NULL),
171     ITEM_FACTORY_ENTRY("/File/_Merge...", NULL, file_merge_cmd_cb, 0, NULL, NULL),
172     ITEM_FACTORY_STOCK_ENTRY("/File/_Close", "<control>W", file_close_cmd_cb,
173                              0, GTK_STOCK_CLOSE),
174     ITEM_FACTORY_ENTRY("/File/<separator>", NULL, NULL, 0, "<Separator>", NULL),
175     ITEM_FACTORY_STOCK_ENTRY("/File/_Save", "<control>S", file_save_cmd_cb,
176                              0, GTK_STOCK_SAVE),
177     ITEM_FACTORY_STOCK_ENTRY("/File/Save _As...", "<shift><control>S", file_save_as_cmd_cb,
178                              0, GTK_STOCK_SAVE_AS),
179     ITEM_FACTORY_ENTRY("/File/<separator>", NULL, NULL, 0, "<Separator>", NULL),
180     ITEM_FACTORY_ENTRY("/File/_Export", NULL, NULL, 0, "<Branch>", NULL),
181     ITEM_FACTORY_ENTRY("/File/Export/as \"Plain _Text\" file...", NULL, export_text_cmd_cb,
182                              0, NULL, NULL),
183     ITEM_FACTORY_ENTRY("/File/Export/as \"_PostScript\" file...", NULL, export_ps_cmd_cb,
184                              0, NULL, NULL),
185     ITEM_FACTORY_ENTRY("/File/Export/as XML - \"P_SML\" (packet summary) file...", NULL, export_psml_cmd_cb,
186                              0, NULL, NULL),
187     ITEM_FACTORY_ENTRY("/File/Export/as XML - \"P_DML\" (packet details) file...", NULL, export_pdml_cmd_cb,
188                              0, NULL, NULL),
189     ITEM_FACTORY_ENTRY("/File/Export/<separator>", NULL, NULL, 0, "<Separator>", NULL),
190     ITEM_FACTORY_ENTRY("/File/Export/Selected Packet _Bytes...", "<control>H", savehex_cb,
191                              0, NULL, NULL),
192     ITEM_FACTORY_ENTRY("/File/<separator>", NULL, NULL, 0, "<Separator>", NULL),
193     ITEM_FACTORY_STOCK_ENTRY("/File/_Print...", "<control>P", file_print_cmd_cb,
194                              0, GTK_STOCK_PRINT),
195     ITEM_FACTORY_ENTRY("/File/<separator>", NULL, NULL, 0, "<Separator>", NULL),
196     ITEM_FACTORY_STOCK_ENTRY("/File/_Quit", "<control>Q", file_quit_cmd_cb,
197                              0, GTK_STOCK_QUIT),
198     ITEM_FACTORY_ENTRY("/_Edit", NULL, NULL, 0, "<Branch>", NULL),
199 #if 0
200     /* Un-#if this when we actually implement Cut/Copy/Paste. */
201     ITEM_FACTORY_STOCK_ENTRY("/Edit/Cut", "<control>X", NULL,
202                              0, GTK_STOCK_CUT),
203     ITEM_FACTORY_STOCK_ENTRY("/Edit/Copy", "<control>C", NULL,
204                              0, GTK_STOCK_COPY),
205     ITEM_FACTORY_STOCK_ENTRY("/Edit/Paste", "<control>V", NULL,
206                              0, GTK_STOCK_PASTE),
207     ITEM_FACTORY_ENTRY("/Edit/<separator>", NULL, NULL, 0, "<Separator>"),
208 #endif
209     ITEM_FACTORY_STOCK_ENTRY("/Edit/_Find Packet...", "<control>F",
210                              find_frame_cb, 0, GTK_STOCK_FIND),
211     ITEM_FACTORY_ENTRY("/Edit/Find Ne_xt", "<control>N", find_next_cb, 0, NULL, NULL),
212     ITEM_FACTORY_ENTRY("/Edit/Find Pre_vious", "<control>B", find_previous_cb, 0, NULL, NULL),
213     ITEM_FACTORY_ENTRY("/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL),
214     ITEM_FACTORY_ENTRY("/Edit/_Time Reference", NULL, NULL, 0, "<Branch>", NULL),
215     ITEM_FACTORY_ENTRY("/Edit/Time Reference/Set Time Reference (toggle)", "<control>T", reftime_frame_cb, REFTIME_TOGGLE, NULL, NULL),
216     ITEM_FACTORY_ENTRY("/Edit/Time Reference/Find Next", NULL, reftime_frame_cb, REFTIME_FIND_NEXT, NULL, NULL),
217     ITEM_FACTORY_ENTRY("/Edit/Time Reference/Find Previous", NULL, reftime_frame_cb, REFTIME_FIND_PREV, NULL, NULL),
218     ITEM_FACTORY_ENTRY("/Edit/_Mark Packet (toggle)", "<control>M", mark_frame_cb,
219                        0, NULL, NULL),
220     ITEM_FACTORY_ENTRY("/Edit/Mark _All Packets", NULL, mark_all_frames_cb,
221                        0, NULL, NULL),
222     ITEM_FACTORY_ENTRY("/Edit/_Unmark All Packets", NULL, unmark_all_frames_cb,
223                        0, NULL, NULL),
224     ITEM_FACTORY_ENTRY("/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL),
225     ITEM_FACTORY_STOCK_ENTRY("/Edit/_Preferences...", "<shift><control>P", prefs_cb,
226                              0, GTK_STOCK_PREFERENCES),
227     ITEM_FACTORY_ENTRY("/_View", NULL, NULL, 0, "<Branch>", NULL),
228     ITEM_FACTORY_ENTRY("/View/_Main Toolbar", NULL, main_toolbar_show_cb, 0, "<CheckItem>", NULL),
229     ITEM_FACTORY_ENTRY("/View/_Filter Toolbar", NULL, filter_toolbar_show_cb, 0, "<CheckItem>", NULL),
230     ITEM_FACTORY_ENTRY("/View/_Statusbar", NULL, statusbar_show_cb, 0, "<CheckItem>", NULL),
231     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
232     ITEM_FACTORY_ENTRY("/View/Packet _List", NULL, packet_list_show_cb, 0, "<CheckItem>", NULL),
233     ITEM_FACTORY_ENTRY("/View/Packet _Details", NULL, tree_view_show_cb, 0, "<CheckItem>", NULL),
234     ITEM_FACTORY_ENTRY("/View/Packet _Bytes", NULL, byte_view_show_cb, 0, "<CheckItem>", NULL),
235     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
236     ITEM_FACTORY_ENTRY("/View/_Time Display Format", NULL, NULL, 0, "<Branch>", NULL),
237     ITEM_FACTORY_ENTRY("/View/Time Display Format/Time of Day", NULL, timestamp_absolute_cb, 
238                         0, "<RadioItem>", NULL),
239     ITEM_FACTORY_ENTRY("/View/Time Display Format/Date and Time of Day", NULL, timestamp_absolute_date_cb, 
240                         0, "/View/Time Display Format/Time of Day", NULL),
241     ITEM_FACTORY_ENTRY("/View/Time Display Format/Seconds Since Beginning of Capture", NULL, timestamp_relative_cb, 
242                         0, "/View/Time Display Format/Time of Day", NULL),
243     ITEM_FACTORY_ENTRY("/View/Time Display Format/Seconds Since Previous Packet", NULL, timestamp_delta_cb, 
244                         0, "/View/Time Display Format/Time of Day", NULL),
245     ITEM_FACTORY_ENTRY("/View/Name Resol_ution", NULL, NULL, 0, "<Branch>", NULL),
246     ITEM_FACTORY_ENTRY("/View/Name Resolution/_Resolve Name", NULL, resolve_name_cb, 0, NULL, NULL),
247     ITEM_FACTORY_ENTRY("/View/Name Resolution/<separator>", NULL, NULL, 0, "<Separator>", NULL),
248     ITEM_FACTORY_ENTRY("/View/Name Resolution/Enable for _MAC Layer", NULL, name_resolution_mac_cb, 0, "<CheckItem>", NULL),
249     ITEM_FACTORY_ENTRY("/View/Name Resolution/Enable for _Network Layer", NULL, name_resolution_network_cb, 0, "<CheckItem>", NULL),
250     ITEM_FACTORY_ENTRY("/View/Name Resolution/Enable for _Transport Layer", NULL, name_resolution_transport_cb, 0, "<CheckItem>", NULL),
251 #ifdef HAVE_LIBPCAP
252     ITEM_FACTORY_ENTRY("/View/Auto Scroll in Li_ve Capture", NULL, auto_scroll_live_cb, 0, "<CheckItem>", NULL),
253 #endif
254     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
255     ITEM_FACTORY_STOCK_ENTRY("/View/_Zoom In", "<control>plus", view_zoom_in_cb,
256                              0, GTK_STOCK_ZOOM_IN),
257     ITEM_FACTORY_STOCK_ENTRY("/View/Zoom _Out", "<control>minus", view_zoom_out_cb,
258                              0, GTK_STOCK_ZOOM_OUT),
259     ITEM_FACTORY_STOCK_ENTRY("/View/_Normal Size", "<control>equal", view_zoom_100_cb,
260                              0, GTK_STOCK_ZOOM_100),
261     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
262     ITEM_FACTORY_ENTRY("/View/Resize Columns", NULL, packet_list_resize_columns_cb,
263                        0, NULL, NULL),
264     ITEM_FACTORY_ENTRY("/View/Collapse _All", NULL, collapse_all_cb,
265                        0, NULL, NULL),
266     ITEM_FACTORY_ENTRY("/View/_Expand All", NULL, expand_all_cb,
267                        0, NULL, NULL),
268     ITEM_FACTORY_ENTRY("/View/Expand Tree", NULL, expand_tree_cb, 0, NULL, NULL),
269     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
270     ITEM_FACTORY_STOCK_ENTRY("/View/_Coloring Rules...", NULL, color_display_cb,
271                        0, GTK_STOCK_SELECT_COLOR),
272     ITEM_FACTORY_ENTRY("/View/<separator>", NULL, NULL, 0, "<Separator>", NULL),
273     ITEM_FACTORY_ENTRY("/View/Show Packet in New _Window", NULL,
274                        new_window_cb, 0, NULL, NULL),
275     ITEM_FACTORY_STOCK_ENTRY("/View/_Reload", "<control>R", file_reload_cmd_cb,
276                              0, GTK_STOCK_REFRESH),
277     ITEM_FACTORY_ENTRY("/_Go", NULL, NULL, 0, "<Branch>", NULL),
278     ITEM_FACTORY_STOCK_ENTRY("/Go/_Back", "<alt>Left",
279                              history_back_cb, 0, GTK_STOCK_GO_BACK),
280     ITEM_FACTORY_STOCK_ENTRY("/Go/_Forward", "<alt>Right",
281                              history_forward_cb, 0, GTK_STOCK_GO_FORWARD),
282     ITEM_FACTORY_STOCK_ENTRY("/Go/_Go to Packet...", "<control>G",
283                              goto_frame_cb, 0, GTK_STOCK_JUMP_TO),
284     ITEM_FACTORY_ENTRY("/Go/Go to _Corresponding Packet", NULL, goto_framenum_cb,
285                        0, NULL, NULL),
286     ITEM_FACTORY_ENTRY("/Go/<separator>", NULL, NULL, 0, "<Separator>", NULL),
287     ITEM_FACTORY_STOCK_ENTRY("/Go/F_irst Packet", NULL,
288                              goto_top_frame_cb, 0, GTK_STOCK_GOTO_TOP),
289     ITEM_FACTORY_STOCK_ENTRY("/Go/_Last Packet", NULL,
290                              goto_bottom_frame_cb, 0, GTK_STOCK_GOTO_BOTTOM),
291 #ifdef HAVE_LIBPCAP
292     ITEM_FACTORY_ENTRY("/_Capture", NULL, NULL, 0, "<Branch>", NULL),
293     ITEM_FACTORY_STOCK_ENTRY("/Capture/_Start...", "<control>K",
294                              capture_prep_cb, 0, ETHEREAL_STOCK_CAPTURE_START),
295     ITEM_FACTORY_STOCK_ENTRY("/Capture/S_top", "<control>E", capture_stop_cb,
296                              0, GTK_STOCK_STOP),
297     ITEM_FACTORY_ENTRY("/Capture/_Interfaces...", NULL,
298                              capture_if_cb, 0, NULL, NULL),
299     ITEM_FACTORY_STOCK_ENTRY("/Capture/_Capture Filters...", NULL, cfilter_dialog_cb,
300                        0, ETHEREAL_STOCK_CAPTURE_FILTER),
301 #endif /* HAVE_LIBPCAP */
302     ITEM_FACTORY_ENTRY("/_Analyze", NULL, NULL, 0, "<Branch>", NULL),
303     ITEM_FACTORY_STOCK_ENTRY("/Analyze/_Display Filters...", NULL, dfilter_dialog_cb,
304                        0, ETHEREAL_STOCK_DISPLAY_FILTER),
305     ITEM_FACTORY_ENTRY("/Analyze/Appl_y as Filter", NULL, NULL, 0, "<Branch>", NULL),
306     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/_Selected", NULL, match_selected_ptree_cb, 
307                        MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
308     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/_Not Selected", NULL, match_selected_ptree_cb, 
309                        MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
310     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/... _and Selected", NULL, match_selected_ptree_cb,
311                        MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
312     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/... _or Selected", NULL, match_selected_ptree_cb,
313                        MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
314     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/... a_nd not Selected", NULL, match_selected_ptree_cb, 
315                        MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
316     ITEM_FACTORY_ENTRY("/Analyze/Apply as Filter/... o_r not Selected", NULL, match_selected_ptree_cb, 
317                        MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
318     ITEM_FACTORY_ENTRY("/Analyze/_Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL),
319     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/_Selected", NULL, match_selected_ptree_cb, 
320                        MATCH_SELECTED_REPLACE, NULL, NULL),
321     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/_Not Selected", NULL, match_selected_ptree_cb, 
322                        MATCH_SELECTED_NOT, NULL, NULL),
323     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/... _and Selected", NULL, match_selected_ptree_cb,
324                        MATCH_SELECTED_AND, NULL, NULL),
325     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/... _or Selected", NULL, match_selected_ptree_cb,
326                        MATCH_SELECTED_OR, NULL, NULL),
327     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/... a_nd not Selected", NULL, match_selected_ptree_cb, 
328                        MATCH_SELECTED_AND_NOT, NULL, NULL),
329     ITEM_FACTORY_ENTRY("/Analyze/Prepare a Filter/... o_r not Selected", NULL, match_selected_ptree_cb, 
330                        MATCH_SELECTED_OR_NOT, NULL, NULL),
331     ITEM_FACTORY_ENTRY("/Analyze/<separator>", NULL, NULL, 0, "<Separator>", NULL),
332     ITEM_FACTORY_ENTRY("/Analyze/_Enabled Protocols...", "<shift><control>R", proto_cb, 0, NULL, NULL),
333     ITEM_FACTORY_ENTRY("/Analyze/Decode _As...", NULL, decode_as_cb,
334                        0, NULL, NULL),
335     ITEM_FACTORY_ENTRY("/Analyze/_User Specified Decodes...", NULL,
336                        decode_show_cb, 0, NULL, NULL),
337     ITEM_FACTORY_ENTRY("/Analyze/<separator>", NULL, NULL, 0, "<Separator>", NULL),
338     ITEM_FACTORY_ENTRY("/Analyze/_Follow TCP Stream", NULL,
339                        follow_stream_cb, 0, NULL, NULL),    
340     ITEM_FACTORY_ENTRY("/_Statistics", NULL, NULL, 0, "<Branch>", NULL),
341     ITEM_FACTORY_ENTRY("/Statistics/_Summary", NULL, summary_open_cb, 0, NULL, NULL),
342     ITEM_FACTORY_ENTRY("/Statistics/_Protocol Hierarchy", NULL,
343                        proto_hier_stats_cb, 0, NULL, NULL),
344     ITEM_FACTORY_ENTRY("/Statistics/Conversations", NULL,
345                        init_conversation_notebook_cb, 0, NULL, NULL),
346     ITEM_FACTORY_ENTRY("/Statistics/Endpoints", NULL,
347                        init_hostlist_notebook_cb, 0, NULL, NULL),
348     ITEM_FACTORY_ENTRY("/_Help", NULL, NULL, 0, "<Branch>", NULL),
349     ITEM_FACTORY_STOCK_ENTRY("/Help/_Contents", "F1", topic_menu_cb, HELP_CONTENT, GTK_STOCK_HELP),
350     ITEM_FACTORY_ENTRY("/Help/_Supported Protocols", NULL, supported_cb, 0, NULL, NULL),
351 #if (GLIB_MAJOR_VERSION >= 2)
352 #ifdef ETHEREAL_EUG_DIR
353     ITEM_FACTORY_ENTRY("/Help/User's Guide", NULL, url_page_menu_cb, HELP_CONTENT, NULL, NULL),
354 #endif
355     ITEM_FACTORY_ENTRY("/Help/Manual Pages", NULL, NULL, 0, "<Branch>", NULL),
356     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Ethereal", NULL, topic_menu_cb, LOCALPAGE_MAN_ETHEREAL, NULL, NULL),
357     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Ethereal Filter", NULL, topic_menu_cb, LOCALPAGE_MAN_ETHEREAL_FILTER, NULL, NULL),
358     ITEM_FACTORY_ENTRY("/Help/Manual Pages/<separator>", NULL, NULL, 0, "<Separator>", NULL),
359     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Tethereal", NULL, topic_menu_cb, LOCALPAGE_MAN_TETHEREAL, NULL, NULL),
360     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Mergecap", NULL, topic_menu_cb, LOCALPAGE_MAN_MERGECAP, NULL, NULL),
361     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Editcap", NULL, topic_menu_cb, LOCALPAGE_MAN_EDITCAP, NULL, NULL),
362     ITEM_FACTORY_ENTRY("/Help/Manual Pages/Text2pcap", NULL, topic_menu_cb, LOCALPAGE_MAN_TEXT2PCAP, NULL, NULL),
363     ITEM_FACTORY_ENTRY("/Help/Ethereal Online", NULL, NULL, 0, "<Branch>", NULL),
364     ITEM_FACTORY_STOCK_ENTRY("/Help/Ethereal Online/Home Page", NULL, topic_menu_cb, ONLINEPAGE_HOME, GTK_STOCK_HOME),
365     ITEM_FACTORY_ENTRY("/Help/Ethereal Online/Wiki", NULL, topic_menu_cb, ONLINEPAGE_WIKI, NULL, NULL),
366     ITEM_FACTORY_ENTRY("/Help/Ethereal Online/User's Guide", NULL, topic_menu_cb, ONLINEPAGE_USERGUIDE, NULL, NULL),
367     ITEM_FACTORY_ENTRY("/Help/Ethereal Online/FAQ's", NULL, topic_menu_cb, ONLINEPAGE_FAQ, NULL, NULL),
368     ITEM_FACTORY_ENTRY("/Help/Ethereal Online/Downloads", NULL, topic_menu_cb, ONLINEPAGE_DOWNLOAD, NULL, NULL),
369     ITEM_FACTORY_ENTRY("/Help/Ethereal Online/Example Files", NULL, topic_menu_cb, ONLINEPAGE_SAMPLE_FILES, NULL, NULL),
370 #endif
371     ITEM_FACTORY_ENTRY("/Help/<separator>", NULL, NULL, 0, "<Separator>", NULL),
372     ITEM_FACTORY_ENTRY("/Help/_About Ethereal", NULL, about_ethereal_cb,
373                        0, NULL, NULL)
374 };
375
376
377 /* calculate the number of menu_items */
378 static int nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
379
380 /* packet list popup */
381 static GtkItemFactoryEntry packet_list_menu_items[] =
382 {
383     ITEM_FACTORY_ENTRY("/Mark Packet (toggle)", NULL, mark_frame_cb, 0, NULL, NULL),
384     ITEM_FACTORY_ENTRY("/Time Reference", NULL, NULL, 0, "<Branch>", NULL),
385     ITEM_FACTORY_ENTRY("/Time Reference/Set Time Reference (toggle)", NULL, reftime_frame_cb, REFTIME_TOGGLE, NULL, NULL),
386     ITEM_FACTORY_ENTRY("/Time Reference/Find Next", NULL, reftime_frame_cb, REFTIME_FIND_NEXT, NULL, NULL),
387     ITEM_FACTORY_ENTRY("/Time Reference/Find Previous", NULL, reftime_frame_cb, REFTIME_FIND_PREV, NULL, NULL),
388
389     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
390
391     ITEM_FACTORY_ENTRY("/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL),
392     ITEM_FACTORY_ENTRY("/Apply as Filter/_Selected", NULL, match_selected_plist_cb, 
393                        MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
394     ITEM_FACTORY_ENTRY("/Apply as Filter/_Not Selected", NULL, match_selected_plist_cb, 
395                        MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
396     ITEM_FACTORY_ENTRY("/Apply as Filter/... _and Selected", NULL, match_selected_plist_cb, 
397                        MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
398     ITEM_FACTORY_ENTRY("/Apply as Filter/... _or Selected", NULL, match_selected_plist_cb, 
399                        MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
400     ITEM_FACTORY_ENTRY("/Apply as Filter/... a_nd not Selected", NULL, match_selected_plist_cb, 
401                        MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
402     ITEM_FACTORY_ENTRY("/Apply as Filter/... o_r not Selected", NULL, match_selected_plist_cb,
403                        MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
404
405     ITEM_FACTORY_ENTRY("/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL),
406     ITEM_FACTORY_ENTRY("/Prepare a Filter/_Selected", NULL, match_selected_plist_cb,
407                        MATCH_SELECTED_REPLACE, NULL, NULL),
408     ITEM_FACTORY_ENTRY("/Prepare a Filter/_Not Selected", NULL, match_selected_plist_cb,
409                        MATCH_SELECTED_NOT, NULL, NULL),
410     ITEM_FACTORY_ENTRY("/Prepare a Filter/... _and Selected", NULL, match_selected_plist_cb,
411                        MATCH_SELECTED_AND, NULL, NULL),
412     ITEM_FACTORY_ENTRY("/Prepare a Filter/... _or Selected", NULL, match_selected_plist_cb,
413                        MATCH_SELECTED_OR, NULL, NULL),
414     ITEM_FACTORY_ENTRY("/Prepare a Filter/... a_nd not Selected", NULL, match_selected_plist_cb,
415                        MATCH_SELECTED_AND_NOT, NULL, NULL),
416     ITEM_FACTORY_ENTRY("/Prepare a Filter/... o_r not Selected", NULL, match_selected_plist_cb,
417                        MATCH_SELECTED_OR_NOT, NULL, NULL),
418
419     ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
420                        0, NULL, NULL),
421
422     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
423
424     ITEM_FACTORY_ENTRY("/Decode As...", NULL, decode_as_cb, 0, NULL, NULL),
425     ITEM_FACTORY_ENTRY("/Print...", NULL, file_print_selected_cmd_cb, 0, NULL, NULL),
426     ITEM_FACTORY_ENTRY("/Show Packet in New Window", NULL, new_window_cb,
427                        0, NULL, NULL)
428 };
429
430 static GtkItemFactoryEntry tree_view_menu_items[] =
431 {
432     ITEM_FACTORY_ENTRY("/Expand Subtrees", NULL, expand_tree_cb, 0, NULL, NULL),
433     ITEM_FACTORY_ENTRY("/Expand All", NULL, expand_all_cb, 0, NULL, NULL),
434     ITEM_FACTORY_ENTRY("/Collapse All", NULL, collapse_all_cb, 0, NULL, NULL),
435
436     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
437
438     ITEM_FACTORY_ENTRY("/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL),
439     ITEM_FACTORY_ENTRY("/Apply as Filter/_Selected", NULL, match_selected_ptree_cb, 
440                        MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
441     ITEM_FACTORY_ENTRY("/Apply as Filter/_Not Selected", NULL, match_selected_ptree_cb,
442                        MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
443     ITEM_FACTORY_ENTRY("/Apply as Filter/... _and Selected", NULL, match_selected_ptree_cb,
444                        MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
445     ITEM_FACTORY_ENTRY("/Apply as Filter/... _or Selected", NULL, match_selected_ptree_cb,
446                        MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
447     ITEM_FACTORY_ENTRY("/Apply as Filter/... a_nd not Selected", NULL, match_selected_ptree_cb, 
448                        MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
449     ITEM_FACTORY_ENTRY("/Apply as Filter/... o_r not Selected", NULL, match_selected_ptree_cb,
450                        MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW, NULL, NULL),
451
452     ITEM_FACTORY_ENTRY("/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL),
453     ITEM_FACTORY_ENTRY("/Prepare a Filter/_Selected", NULL, match_selected_ptree_cb, 
454                        MATCH_SELECTED_REPLACE, NULL, NULL),
455     ITEM_FACTORY_ENTRY("/Prepare a Filter/_Not Selected", NULL, match_selected_ptree_cb,
456                        MATCH_SELECTED_NOT, NULL, NULL),
457     ITEM_FACTORY_ENTRY("/Prepare a Filter/... _and Selected", NULL, match_selected_ptree_cb,
458                        MATCH_SELECTED_AND, NULL, NULL),
459     ITEM_FACTORY_ENTRY("/Prepare a Filter/... _or Selected", NULL, match_selected_ptree_cb,
460                        MATCH_SELECTED_OR, NULL, NULL),
461     ITEM_FACTORY_ENTRY("/Prepare a Filter/... a_nd not Selected", NULL, match_selected_ptree_cb, 
462                        MATCH_SELECTED_AND_NOT, NULL, NULL),
463     ITEM_FACTORY_ENTRY("/Prepare a Filter/... o_r not Selected", NULL, match_selected_ptree_cb,
464                        MATCH_SELECTED_OR_NOT, NULL, NULL),
465
466     ITEM_FACTORY_ENTRY("/Follow TCP Stream", NULL, follow_stream_cb,
467                        0, NULL, NULL),
468
469     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
470
471     ITEM_FACTORY_STOCK_ENTRY("/Wiki Protocol Page", NULL, selected_ptree_info_cb,
472                        0, GTK_STOCK_HOME),
473     ITEM_FACTORY_STOCK_ENTRY("/Filter Field Reference", NULL, selected_ptree_ref_cb,
474                        0, GTK_STOCK_HOME),
475     ITEM_FACTORY_ENTRY("/Protocol Preferences...", NULL, properties_cb,
476                        0, NULL, NULL),
477     ITEM_FACTORY_ENTRY("/<separator>", NULL, NULL, 0, "<Separator>", NULL),
478     ITEM_FACTORY_ENTRY("/Decode As...", NULL, decode_as_cb, 0, NULL, NULL),
479     ITEM_FACTORY_ENTRY("/_Resolve Name", NULL, resolve_name_cb, 0, NULL, NULL),
480     ITEM_FACTORY_ENTRY("/_Go to Corresponding Packet", NULL, goto_framenum_cb, 0, NULL, NULL),
481 };
482
483 static GtkItemFactoryEntry hexdump_menu_items[] =
484 {
485     ITEM_FACTORY_ENTRY("/Copy", NULL, copy_hex_cb,
486                        0, NULL, NULL),
487     ITEM_FACTORY_ENTRY("/Export Selected Packet Bytes...", NULL, savehex_cb,
488                        0, NULL, NULL),
489 };
490
491 static int initialize = TRUE;
492 static GtkItemFactory *main_menu_factory = NULL;
493 static GtkItemFactory *packet_list_menu_factory = NULL;
494 static GtkItemFactory *tree_view_menu_factory = NULL;
495 static GtkItemFactory *hexdump_menu_factory = NULL;
496
497 static GSList *popup_menu_list = NULL;
498
499 static GtkAccelGroup *grp;
500
501 GtkWidget *
502 main_menu_new(GtkAccelGroup ** table) {
503   GtkWidget *menubar;
504
505   grp = gtk_accel_group_new();
506
507   if (initialize)
508     menus_init();
509
510   menubar = main_menu_factory->widget;
511
512   if (table)
513     *table = grp;
514
515   return menubar;
516 }
517
518 static void
519 menus_init(void) {
520   if (initialize) {
521     initialize = FALSE;
522
523     /* popup */
524     packet_list_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
525     popup_menu_object = gtk_menu_new();
526     gtk_item_factory_create_items_ac(packet_list_menu_factory, sizeof(packet_list_menu_items)/sizeof(packet_list_menu_items[0]), packet_list_menu_items, popup_menu_object, 2);
527     OBJECT_SET_DATA(popup_menu_object, PM_PACKET_LIST_KEY,
528                     packet_list_menu_factory->widget);
529     popup_menu_list = g_slist_append((GSList *)popup_menu_list, packet_list_menu_factory);
530
531     tree_view_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
532     gtk_item_factory_create_items_ac(tree_view_menu_factory, sizeof(tree_view_menu_items)/sizeof(tree_view_menu_items[0]), tree_view_menu_items, popup_menu_object, 2);
533     OBJECT_SET_DATA(popup_menu_object, PM_TREE_VIEW_KEY,
534                     tree_view_menu_factory->widget);
535     popup_menu_list = g_slist_append((GSList *)popup_menu_list, tree_view_menu_factory);
536
537     hexdump_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
538     gtk_item_factory_create_items_ac(hexdump_menu_factory, sizeof(hexdump_menu_items)/sizeof(hexdump_menu_items[0]), hexdump_menu_items, popup_menu_object, 2);
539     OBJECT_SET_DATA(popup_menu_object, PM_HEXDUMP_KEY,
540                     hexdump_menu_factory->widget);
541     popup_menu_list = g_slist_append((GSList *)popup_menu_list, hexdump_menu_factory);
542
543     /* main */
544     main_menu_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", grp);
545     gtk_item_factory_create_items_ac(main_menu_factory, nmenu_items, menu_items, NULL, 2);
546
547     merge_all_tap_menus(tap_menu_tree_root);
548
549     /* Initialize enabled/disabled state of menu items */
550     set_menus_for_unsaved_capture_file(FALSE);
551     set_menus_for_capture_file(FALSE);
552 #if 0
553     /* Un-#if this when we actually implement Cut/Copy/Paste.
554        Then make sure you enable them when they can be done. */
555     set_menu_sensitivity(main_menu_factory, "/Edit/Cut", FALSE);
556     set_menu_sensitivity(main_menu_factory, "/Edit/Copy", FALSE);
557     set_menu_sensitivity(main_menu_factory, "/Edit/Paste", FALSE);
558 #endif
559
560     set_menus_for_captured_packets(FALSE);
561     set_menus_for_selected_packet(&cfile);
562     set_menus_for_selected_tree_row(&cfile);
563
564     /* init with an empty recent files list */
565     clear_menu_recent_capture_file_cmd_cb(NULL, NULL);
566   }
567 }
568
569
570 gint tap_menu_item_add_compare(gconstpointer a, gconstpointer b)
571 {
572     return strcmp(
573         ((const menu_item_t *) a)->name, 
574         ((const menu_item_t *) b)->name);
575 }
576
577
578 /* add a menuitem below the current node */
579 GList * tap_menu_item_add(
580     char *name, 
581     gint group, 
582     GtkItemFactoryCallback callback,
583     gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *),
584     gboolean (*selected_tree_row_enabled)(field_info *),
585     gpointer callback_data,
586         GList *curnode)
587 {
588     menu_item_t *curr;
589     menu_item_t *child;
590
591
592     child = g_malloc(sizeof (menu_item_t));
593     child->group            = group;
594     child->name             = name;
595     child->callback         = callback;
596     child->selected_packet_enabled = selected_packet_enabled;
597     child->selected_tree_row_enabled = selected_tree_row_enabled;
598     child->callback_data    = callback_data;
599     child->enabled          = FALSE;
600     child->children         = NULL;
601
602     /* insert the new child node into the parent */
603     curr = curnode->data;
604     curr->children = g_list_insert_sorted(curr->children, child, tap_menu_item_add_compare);
605
606     /* return the new node */
607     /* XXX: improve this */
608     return g_list_find(curr->children, child);
609 }
610
611 /*
612  * Add a new menu item for a tap.
613  * This must be called after we've created the main menu, so it can't
614  * be called from the routine that registers taps - we have to introduce
615  * another per-tap registration routine.
616  *
617  * "callback" gets called when the menu item is selected; it should do
618  * the work of creating the tap window.
619  *
620  * "selected_packet_enabled" gets called by "set_menus_for_selected_packet()";
621  * it's passed a Boolean that's TRUE if a packet is selected and FALSE
622  * otherwise, and should return TRUE if the tap will work now (which
623  * might depend on whether a packet is selected and, if one is, on the
624  * packet) and FALSE if not.
625  *
626  * "selected_tree_row_enabled" gets called by
627  * "set_menus_for_selected_tree_row()"; it's passed a Boolean that's TRUE if
628  * a protocol tree row is selected and FALSE otherwise, and should return
629  * TRUE if the tap will work now (which might depend on whether a tree row
630  * is selected and, if one is, on the tree row) and FALSE if not.
631  */
632 void
633 register_tap_menu_item(
634     char *name, 
635     REGISTER_TAP_GROUP_E group,
636     GtkItemFactoryCallback callback,
637     gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *),
638     gboolean (*selected_tree_row_enabled)(field_info *),
639     gpointer callback_data)
640 {
641     /*static const char toolspath[] = "/Statistics/";*/
642     char *toolspath;
643     char *p;
644     char *menupath;
645     size_t menupathlen;
646     menu_item_t *child;
647     GList *curnode;
648     GList *childnode;
649
650     /*
651      * The menu path must be relative.
652      */
653     g_assert(*name != '/');
654
655     switch(group) {
656     case(REGISTER_TAP_GROUP_GENERIC): toolspath = "/Statistics/"; break;
657     case(REGISTER_TAP_GROUP_CONVERSATION_LIST): toolspath = "/Statistics/_Conversation List/"; break;
658     case(REGISTER_TAP_GROUP_ENDPOINT_LIST): toolspath = "/Statistics/_Endpoint List/"; break;
659     case(REGISTER_TAP_GROUP_RESPONSE_TIME): toolspath = "/Statistics/Service _Response Time/"; break;
660     case(REGISTER_TAP_GROUP_NONE): toolspath = "/Statistics/"; break;
661     default:
662         g_assert(0);
663         toolspath = NULL;
664     }
665
666     /* add the (empty) root node, if not already done */
667     if(tap_menu_tree_root == NULL) {
668         child = g_malloc0(sizeof (menu_item_t));
669         tap_menu_tree_root = g_list_append(NULL, child);
670     }
671
672     /*
673      * Create any submenus required.
674      */
675     curnode = tap_menu_tree_root;
676     p = name;
677     while ((p = strchr(p, '/')) != NULL) {
678         /*
679          * OK, everything between "name" and "p" is
680          * a menu relative subtree into which the menu item
681          * will be placed.
682          *
683          * Construct the absolute path name of that subtree.
684          */
685         menupathlen = strlen(toolspath) + 1 + (p - name);
686         menupath = g_malloc(menupathlen);
687         strcpy(menupath, toolspath);
688         strncat(menupath, name, p - name);
689
690         /*
691          * Does there exist an entry with that path at this
692          * level of the Analyze menu tree?
693          */
694         child = curnode->data;
695         for (childnode = child->children; childnode != NULL; childnode = childnode->next) {
696             child = childnode->data;
697             if (strcmp(child->name, menupath) == 0)
698                 break;
699         }
700         if (childnode == NULL) {
701             /*
702              * No.  Create such an item as a subtree, and
703              * add it to the Tools menu tree.
704              */
705             childnode = tap_menu_item_add(
706                 menupath, group, NULL, NULL ,NULL, NULL, curnode);
707         } else {
708             /*
709              * Yes.  We don't need this "menupath" any longer.
710              */
711             g_free(menupath);
712         }
713         curnode = childnode;
714
715         /*
716          * Skip over the '/' we found.
717          */
718         p++;
719     }
720
721     /*
722      * Construct the main menu path for the menu item.
723      */
724     menupathlen = strlen(toolspath) + 1 + strlen(name);
725     menupath = g_malloc(menupathlen);
726     strcpy(menupath, toolspath);
727     strcat(menupath, name);
728
729     /*
730      * Construct an item factory entry for the item, and add it to
731      * the main menu.
732      */
733     tap_menu_item_add(
734         menupath, group, callback, 
735         selected_packet_enabled, selected_tree_row_enabled, 
736         callback_data, curnode);
737 }
738
739
740 guint merge_tap_menus_layered(GList *node, gint group) {
741     GtkItemFactoryEntry *entry;
742     GList       *child;
743     guint       added = 0;
744     menu_item_t *node_data = node->data;
745
746     /*
747      * Is this a leaf node or an interior node?
748      */
749     if (node_data->children == NULL) {
750         /*
751          * It's a leaf node.
752          */
753
754         /*
755          * The root node doesn't correspond to a menu tree item; it
756          * has a null name pointer.
757          */
758         if (node_data->name != NULL && group == node_data->group) {
759             entry = g_malloc0(sizeof (GtkItemFactoryEntry));
760             entry->path = node_data->name;
761             entry->callback = node_data->callback;
762             gtk_item_factory_create_item(main_menu_factory, entry, node_data->callback_data, 2);
763             set_menu_sensitivity(main_menu_factory, node_data->name, FALSE); /* no capture file yet */
764             added++;
765         }
766     } else {
767         /*
768          * It's an interior node; call
769          * "merge_tap_menus_layered()" on all its children 
770          */
771
772         /*
773          * The root node doesn't correspond to a menu tree item; it
774          * has a null name pointer.
775          */
776         if (node_data->name != NULL && group == node_data->group) {
777             entry = g_malloc0(sizeof (GtkItemFactoryEntry));
778             entry->path = node_data->name;
779             entry->item_type = "<Branch>";
780             gtk_item_factory_create_item(main_menu_factory, entry,
781                 NULL, 2);
782             set_menu_sensitivity(main_menu_factory, node_data->name,
783                 FALSE);    /* no children yet */
784             added++;
785         }
786
787         for (child = node_data->children; child != NULL; child =
788             child->next) {
789             added += merge_tap_menus_layered(child, group);
790         }
791     }
792
793     return added;
794 }
795
796
797 void merge_all_tap_menus(GList *node) {
798     GtkItemFactoryEntry *entry;
799
800     entry = g_malloc0(sizeof (GtkItemFactoryEntry));
801     entry->item_type = "<Separator>";
802     entry->path = "/Statistics/";
803
804     /* 
805      * merge only the menu items of the specific group,
806      * and then append a seperator
807      */
808     if (merge_tap_menus_layered(node, REGISTER_TAP_GROUP_GENERIC)) {
809         gtk_item_factory_create_item(main_menu_factory, entry, NULL, 2);
810     }
811     if (merge_tap_menus_layered(node, REGISTER_TAP_GROUP_CONVERSATION_LIST)) {
812         /*gtk_item_factory_create_item(main_menu_factory, entry, NULL, 2);*/
813     }
814     if (merge_tap_menus_layered(node, REGISTER_TAP_GROUP_ENDPOINT_LIST)) {
815         /*gtk_item_factory_create_item(main_menu_factory, entry, NULL, 2);*/
816     }
817     if (merge_tap_menus_layered(node, REGISTER_TAP_GROUP_RESPONSE_TIME)) {
818         gtk_item_factory_create_item(main_menu_factory, entry, NULL, 2);
819     }
820     if (merge_tap_menus_layered(node, REGISTER_TAP_GROUP_NONE)) {
821         /*gtk_item_factory_create_item(main_menu_factory, entry, NULL, 2);*/
822     }
823 }
824
825
826
827 /*
828  * Enable/disable menu sensitivity.
829  */
830 static void
831 set_menu_sensitivity(GtkItemFactory *ifactory, const gchar *path, gint val)
832 {
833   GSList *menu_list;
834   GtkWidget *menu_item;
835   gchar *dup;
836   gchar *dest;
837
838
839   /* the underscore character regularly confuses things, as it will prevent finding 
840    * the menu_item, so it has to be removed first */
841   dup = g_strdup(path);
842   dest = dup;
843   while(*path) {
844       if (*path != '_') {
845         *dest = *path;
846         dest++;
847       }
848       path++;
849   }
850   *dest = '\0';
851
852   if (ifactory == NULL) {
853     /*
854      * Do it for all pop-up menus.
855      */
856     for (menu_list = popup_menu_list; menu_list != NULL;
857          menu_list = g_slist_next(menu_list))
858       set_menu_sensitivity(menu_list->data, dup, val);
859   } else {
860     /*
861      * Do it for that particular menu.
862      */
863     if ((menu_item = gtk_item_factory_get_widget(ifactory, dup)) != NULL) {
864       if (GTK_IS_MENU(menu_item)) {
865         /*
866          * "dup" refers to a submenu; "gtk_item_factory_get_widget()"
867          * gets the menu, not the item that, when selected, pops up
868          * the submenu.
869          *
870          * We have to change the latter item's sensitivity, so that
871          * it shows up normally if sensitive and grayed-out if
872          * insensitive.
873          */
874         menu_item = gtk_menu_get_attach_widget(GTK_MENU(menu_item));
875       }
876       gtk_widget_set_sensitive(menu_item, val);
877     } else{
878       /* be sure this menu item *is* existing */
879       g_assert_not_reached();
880     }
881   }
882
883   g_free(dup);
884 }
885
886 void
887 set_menu_object_data_meat(GtkItemFactory *ifactory, gchar *path, gchar *key, gpointer data)
888 {
889         GtkWidget *menu = NULL;
890
891         if ((menu = gtk_item_factory_get_widget(ifactory, path)) != NULL)
892                 OBJECT_SET_DATA(menu, key, data);
893 }
894
895 void
896 set_menu_object_data (gchar *path, gchar *key, gpointer data) {
897   GSList *menu_list = popup_menu_list;
898   gchar *shortpath = strrchr(path, '/');
899
900   set_menu_object_data_meat(main_menu_factory, path, key, data);
901   while (menu_list != NULL) {
902         set_menu_object_data_meat(menu_list->data, shortpath, key, data);
903         menu_list = g_slist_next(menu_list);
904   }
905 }
906
907
908 /* Recently used capture files submenu: 
909  * Submenu containing the recently used capture files.
910  * The capture filenames are always kept with the absolute path, to be independant
911  * of the current path. 
912  * They are only stored inside the labels of the submenu (no separate list). */
913
914 #define MENU_RECENT_FILES_PATH "/File/Open Recent"
915 #define MENU_RECENT_FILES_KEY "Recent File Name"
916
917 void
918 update_menu_recent_capture_file1(GtkWidget *widget, gpointer cnt) {
919     gchar *widget_cf_name;
920
921     widget_cf_name = OBJECT_GET_DATA(widget, MENU_RECENT_FILES_KEY);
922
923     /* if this menu item is a file, count it */
924     if (widget_cf_name) {
925         (*(guint *)cnt)++;
926     }
927 }
928
929
930 /* update the menu */
931 void
932 update_menu_recent_capture_file(GtkWidget *submenu_recent_files) {
933     guint cnt = 0;
934
935     gtk_container_foreach(GTK_CONTAINER(submenu_recent_files), 
936                 update_menu_recent_capture_file1, &cnt);
937
938     /* make parent menu item sensitive only, if we have any valid files in the list */
939     set_menu_sensitivity(main_menu_factory, MENU_RECENT_FILES_PATH, cnt);
940 }
941
942
943 /* remove the capture filename from the "Recent Files" menu */
944 void
945 remove_menu_recent_capture_file(GtkWidget *widget, gpointer unused _U_) {
946     GtkWidget *submenu_recent_files;
947     gchar *widget_cf_name;
948
949
950     widget_cf_name = OBJECT_GET_DATA(widget, MENU_RECENT_FILES_KEY);
951     g_free(widget_cf_name);
952
953     /* get the submenu container item */
954     submenu_recent_files = gtk_item_factory_get_widget(main_menu_factory, MENU_RECENT_FILES_PATH);
955
956     /* XXX: is this all we need to do, to free the menu item and its label?
957        The reference count of widget will go to 0, so it'll be freed;
958        will that free the label? */
959     gtk_container_remove(GTK_CONTAINER(submenu_recent_files), widget);
960 }
961
962
963 /* callback, if the user pushed the <Clear File List> item */
964 static void
965 clear_menu_recent_capture_file_cmd_cb(GtkWidget *w _U_, gpointer unused _U_) {
966     GtkWidget *submenu_recent_files;
967
968
969     submenu_recent_files = gtk_item_factory_get_widget(main_menu_factory, MENU_RECENT_FILES_PATH);
970
971     gtk_container_foreach(GTK_CONTAINER(submenu_recent_files), 
972                 remove_menu_recent_capture_file, NULL);
973
974     update_menu_recent_capture_file(submenu_recent_files);
975 }
976
977
978 /* callback, if the user pushed a recent file submenu item */
979 void
980 menu_open_recent_file_cmd(GtkWidget *w)
981 {
982         GtkWidget *submenu_recent_files;
983         GtkWidget *menu_item_child;
984         gchar     *cf_name;
985         int       err;
986
987         submenu_recent_files = gtk_item_factory_get_widget(main_menu_factory, MENU_RECENT_FILES_PATH);
988
989         /* get capture filename from the menu item label */
990         menu_item_child = (GTK_BIN(w))->child;
991         gtk_label_get(GTK_LABEL(menu_item_child), &cf_name);
992
993         /* open and read the capture file (this will close an existing file) */
994         if (cf_open(&cfile, cf_name, FALSE, &err) == CF_OK) {
995                 cf_read(&cfile);
996         } else {
997                 /* the capture file isn't existing any longer, remove menu item */
998                 /* XXX: ask user to remove item, it's maybe only a temporary problem */
999                 remove_menu_recent_capture_file(w, NULL);
1000         }
1001
1002         update_menu_recent_capture_file(submenu_recent_files);
1003 }
1004
1005 static void menu_open_recent_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
1006 {
1007     switch(btn) {
1008     case(ESD_BTN_YES):
1009         /* save file first */
1010         file_save_as_cmd(after_save_open_recent_file, data);
1011         break;
1012     case(ESD_BTN_NO):
1013         cf_close(&cfile);
1014         menu_open_recent_file_cmd(data);
1015         break;
1016     case(ESD_BTN_CANCEL):
1017         break;
1018     default:
1019         g_assert_not_reached();
1020     }
1021 }
1022
1023 void
1024 menu_open_recent_file_cmd_cb(GtkWidget *widget, gpointer data _U_) {
1025   gpointer  dialog;
1026
1027
1028   if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
1029     /* user didn't saved his current file, ask him */
1030     dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
1031                 PRIMARY_TEXT_START "Save capture file before opening a new one?" PRIMARY_TEXT_END "\n\n"
1032                 "If you open a new capture file without saving, your current capture data will be discarded.");
1033     simple_dialog_set_cb(dialog, menu_open_recent_file_answered_cb, widget);
1034   } else {
1035     /* unchanged file */
1036     menu_open_recent_file_cmd(widget);
1037   }
1038 }
1039
1040 /* add the capture filename (with an absolute path) to the "Recent Files" menu */
1041 void
1042 add_menu_recent_capture_file_absolute(gchar *cf_name) {
1043         GtkWidget *submenu_recent_files;
1044         GList *menu_item_list;
1045         GList *li;
1046         gchar *widget_cf_name;
1047         gchar *normalized_cf_name;
1048         GtkWidget *menu_item;
1049         guint cnt;
1050
1051
1052
1053         normalized_cf_name = g_strdup(cf_name);
1054 #ifdef _WIN32
1055         /* replace all slashes by backslashes */
1056         g_strdelimit(normalized_cf_name, "/", '\\');
1057 #endif
1058
1059         /* get the submenu container item */
1060         submenu_recent_files = gtk_item_factory_get_widget(main_menu_factory, MENU_RECENT_FILES_PATH);
1061
1062         /* convert container to a GList */
1063         menu_item_list = gtk_container_children(GTK_CONTAINER(submenu_recent_files));
1064
1065         /* iterate through list items of menu_item_list, 
1066          * removing special items, a maybe duplicate entry and every item above count_max */
1067         cnt = 1;
1068         for (li = g_list_first(menu_item_list); li; li = li->next, cnt++) {
1069                 /* get capture filename */
1070                 menu_item = GTK_WIDGET(li->data);
1071                 widget_cf_name = OBJECT_GET_DATA(menu_item, MENU_RECENT_FILES_KEY);
1072
1073                 /* if this element string is one of our special items (seperator, ...) or
1074                  * already in the list or 
1075                  * this element is above maximum count (too old), remove it */
1076                 if (!widget_cf_name ||
1077 #ifdef _WIN32
1078                     /* do a case insensitive compare on win32 */
1079 #if GLIB_MAJOR_VERSION < 2
1080                     g_strncasecmp(widget_cf_name, normalized_cf_name, 1000) == 0 ||
1081 #else
1082                     g_ascii_strncasecmp(widget_cf_name, normalized_cf_name, 1000) == 0 ||
1083 #endif
1084 #else   /* _WIN32 */
1085                     /* do a case sensitive compare on unix */
1086                     strncmp(widget_cf_name, normalized_cf_name, 1000) == 0 ||
1087 #endif
1088                     cnt >= prefs.gui_recent_files_count_max) {
1089                         remove_menu_recent_capture_file(li->data, NULL);
1090                         cnt--;
1091                 }
1092         }
1093
1094         g_list_free(menu_item_list);
1095
1096         /* add new item at latest position */
1097         menu_item = gtk_menu_item_new_with_label(normalized_cf_name);
1098         OBJECT_SET_DATA(menu_item, MENU_RECENT_FILES_KEY, normalized_cf_name);
1099         gtk_menu_prepend (GTK_MENU(submenu_recent_files), menu_item);
1100         SIGNAL_CONNECT_OBJECT(GTK_OBJECT(menu_item), "activate", 
1101                 menu_open_recent_file_cmd_cb, (GtkObject *) menu_item);
1102         gtk_widget_show (menu_item);
1103
1104         /* add seperator at last position */
1105         menu_item = gtk_menu_item_new();
1106         gtk_menu_append (GTK_MENU(submenu_recent_files), menu_item);
1107         gtk_widget_show (menu_item);
1108
1109         /* add new "clear list" item at last position */
1110 #if GTK_MAJOR_VERSION < 2
1111         menu_item = gtk_menu_item_new_with_label("<Clear File List>");
1112 #else
1113         menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
1114 #endif
1115         gtk_menu_append (GTK_MENU(submenu_recent_files), menu_item);
1116         SIGNAL_CONNECT_OBJECT(GTK_OBJECT(menu_item), "activate", 
1117                 clear_menu_recent_capture_file_cmd_cb, (GtkObject *) menu_item);
1118         gtk_widget_show (menu_item);
1119
1120         update_menu_recent_capture_file(submenu_recent_files);
1121 }
1122
1123
1124 /* add the capture filename to the "Recent Files" menu */
1125 /* (will change nothing, if this filename is already in the menu) */
1126 void
1127 add_menu_recent_capture_file(gchar *cf_name) {
1128         gchar *curr;
1129         gchar *absolute;
1130         
1131         
1132         /* if this filename is an absolute path, we can use it directly */
1133         if (g_path_is_absolute(cf_name)) {
1134                 add_menu_recent_capture_file_absolute(cf_name);
1135                 return;
1136         }
1137
1138         /* this filename is not an absolute path, prepend the current dir */
1139         curr = g_get_current_dir();
1140         absolute = g_strdup_printf("%s%s%s", curr, G_DIR_SEPARATOR_S, cf_name);
1141         add_menu_recent_capture_file_absolute(absolute);
1142         g_free(curr);
1143         g_free(absolute);
1144 }
1145
1146
1147 /* write all capture filenames of the menu to the user's recent file */
1148 void
1149 menu_recent_file_write_all(FILE *rf) {
1150     GtkWidget   *submenu_recent_files;
1151     GList       *children;
1152     GList       *child;
1153     gchar       *cf_name;
1154
1155
1156     submenu_recent_files = gtk_item_factory_get_widget(main_menu_factory, MENU_RECENT_FILES_PATH);
1157
1158     /* we have to iterate backwards through the children's list,
1159      * so we get the latest item last in the file.
1160      * (don't use gtk_container_foreach() here, it will return the wrong iteration order) */
1161     children = gtk_container_children(GTK_CONTAINER(submenu_recent_files));
1162     child = g_list_last(children);
1163     while(child != NULL) {
1164         /* get capture filename from the menu item label */
1165         cf_name = OBJECT_GET_DATA(child->data, MENU_RECENT_FILES_KEY);
1166         if (cf_name) {
1167             fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", cf_name);
1168         }
1169
1170         child = g_list_previous(child);
1171     }
1172
1173     g_list_free(children);
1174 }
1175
1176
1177 static void
1178 main_toolbar_show_cb(GtkWidget *w _U_, gpointer d _U_)
1179 {
1180
1181     /* save current setting in recent */
1182     recent.main_toolbar_show = GTK_CHECK_MENU_ITEM(w)->active;
1183
1184     main_widgets_show_or_hide();
1185 }
1186
1187
1188 static void
1189 filter_toolbar_show_cb(GtkWidget *w _U_, gpointer d _U_)
1190 {
1191
1192     /* save current setting in recent */
1193     recent.filter_toolbar_show = GTK_CHECK_MENU_ITEM(w)->active;
1194
1195     main_widgets_show_or_hide();
1196 }
1197
1198
1199 static void
1200 packet_list_show_cb(GtkWidget *w _U_, gpointer d _U_)
1201 {
1202
1203     /* save current setting in recent */
1204     recent.packet_list_show = GTK_CHECK_MENU_ITEM(w)->active;
1205
1206     main_widgets_show_or_hide();
1207 }
1208
1209
1210 static void
1211 tree_view_show_cb(GtkWidget *w _U_, gpointer d _U_)
1212 {
1213
1214     /* save current setting in recent */
1215     recent.tree_view_show = GTK_CHECK_MENU_ITEM(w)->active;
1216
1217     main_widgets_show_or_hide();
1218 }
1219
1220
1221 static void
1222 byte_view_show_cb(GtkWidget *w _U_, gpointer d _U_)
1223 {
1224
1225     /* save current setting in recent */
1226     recent.byte_view_show = GTK_CHECK_MENU_ITEM(w)->active;
1227
1228     main_widgets_show_or_hide();
1229 }
1230
1231
1232 static void
1233 statusbar_show_cb(GtkWidget *w _U_, gpointer d _U_)
1234 {
1235
1236     /* save current setting in recent */
1237     recent.statusbar_show = GTK_CHECK_MENU_ITEM(w)->active;
1238
1239     main_widgets_show_or_hide();
1240 }
1241
1242
1243 static void 
1244 timestamp_absolute_cb(GtkWidget *w _U_, gpointer d _U_)
1245 {
1246     if (recent.gui_time_format != TS_ABSOLUTE) {
1247         set_timestamp_setting(TS_ABSOLUTE);
1248         recent.gui_time_format  = TS_ABSOLUTE;
1249         cf_change_time_formats(&cfile);
1250     }
1251 }
1252
1253 static void 
1254 timestamp_absolute_date_cb(GtkWidget *w _U_, gpointer d _U_)
1255 {
1256     if (recent.gui_time_format != TS_ABSOLUTE_WITH_DATE) {
1257         set_timestamp_setting(TS_ABSOLUTE_WITH_DATE);
1258         recent.gui_time_format  = TS_ABSOLUTE_WITH_DATE;
1259         cf_change_time_formats(&cfile);
1260     }
1261 }
1262
1263 static void 
1264 timestamp_relative_cb(GtkWidget *w _U_, gpointer d _U_)
1265 {
1266     if (recent.gui_time_format != TS_RELATIVE) {
1267         set_timestamp_setting(TS_RELATIVE);
1268         recent.gui_time_format  = TS_RELATIVE;
1269         cf_change_time_formats(&cfile);
1270     }
1271 }
1272
1273 static void 
1274 timestamp_delta_cb(GtkWidget *w _U_, gpointer d _U_)
1275 {
1276     if (recent.gui_time_format != TS_DELTA) {
1277         set_timestamp_setting(TS_DELTA);
1278         recent.gui_time_format  = TS_DELTA;
1279         cf_change_time_formats(&cfile);
1280     }
1281 }
1282
1283 void
1284 menu_name_resolution_changed(void)
1285 {
1286     GtkWidget *menu = NULL;
1287
1288     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Name Resolution/Enable for MAC Layer");
1289     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), g_resolv_flags & RESOLV_MAC);
1290
1291     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Name Resolution/Enable for Network Layer");
1292     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), g_resolv_flags & RESOLV_NETWORK);
1293
1294     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Name Resolution/Enable for Transport Layer");
1295     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), g_resolv_flags & RESOLV_TRANSPORT);
1296 }
1297
1298 static void 
1299 name_resolution_mac_cb(GtkWidget *w _U_, gpointer d _U_)
1300 {
1301     if (GTK_CHECK_MENU_ITEM(w)->active) {
1302         g_resolv_flags |= RESOLV_MAC;
1303     } else {
1304         g_resolv_flags &= ~RESOLV_MAC;
1305     }
1306 }
1307
1308 static void 
1309 name_resolution_network_cb(GtkWidget *w _U_, gpointer d _U_)
1310 {
1311     if (GTK_CHECK_MENU_ITEM(w)->active) {
1312         g_resolv_flags |= RESOLV_NETWORK;
1313     } else {
1314         g_resolv_flags &= ~RESOLV_NETWORK;
1315     }
1316 }
1317
1318 static void 
1319 name_resolution_transport_cb(GtkWidget *w _U_, gpointer d _U_)
1320 {
1321     if (GTK_CHECK_MENU_ITEM(w)->active) {
1322         g_resolv_flags |= RESOLV_TRANSPORT;
1323     } else {
1324         g_resolv_flags &= ~RESOLV_TRANSPORT;
1325     }
1326 }
1327
1328 #ifdef HAVE_LIBPCAP
1329 static void 
1330 auto_scroll_live_cb(GtkWidget *w _U_, gpointer d _U_)
1331 {
1332     auto_scroll_live = GTK_CHECK_MENU_ITEM(w)->active;
1333 }
1334 #endif
1335
1336 /* the recent file read has finished, update the menu corresponding */
1337 void
1338 menu_recent_read_finished(void) {
1339     GtkWidget *menu = NULL;
1340
1341     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Main Toolbar");
1342     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.main_toolbar_show);
1343
1344     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Filter Toolbar");
1345     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.filter_toolbar_show);
1346
1347     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Statusbar");
1348     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.statusbar_show);
1349
1350     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Packet List");
1351     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.packet_list_show);
1352
1353     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Packet Details");
1354     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.tree_view_show);
1355
1356     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Packet Bytes");
1357     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.byte_view_show);
1358
1359     menu_name_resolution_changed();
1360
1361 #ifdef HAVE_LIBPCAP
1362     menu = gtk_item_factory_get_widget(main_menu_factory, "/View/Auto Scroll in Live Capture");
1363     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), auto_scroll_live);
1364 #endif
1365
1366     main_widgets_rearrange();
1367
1368     /* don't change the time format, if we had a command line value */
1369     if (get_timestamp_setting() != TS_NOT_SET) {
1370         recent.gui_time_format = get_timestamp_setting();
1371     }
1372
1373     switch(recent.gui_time_format) {
1374     case(TS_ABSOLUTE):
1375         menu = gtk_item_factory_get_widget(main_menu_factory, 
1376             "/View/Time Display Format/Time of Day");
1377         /* set_active will not trigger the callback when activating an active item! */
1378         recent.gui_time_format = -1;
1379         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE);
1380         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
1381         break;
1382     case(TS_ABSOLUTE_WITH_DATE):
1383         menu = gtk_item_factory_get_widget(main_menu_factory, 
1384             "/View/Time Display Format/Date and Time of Day");
1385         recent.gui_time_format = -1;
1386         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
1387         break;
1388     case(TS_RELATIVE):
1389         menu = gtk_item_factory_get_widget(main_menu_factory, 
1390             "/View/Time Display Format/Seconds Since Beginning of Capture");
1391         recent.gui_time_format = -1;
1392         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
1393         break;
1394     case(TS_DELTA):
1395         menu = gtk_item_factory_get_widget(main_menu_factory, 
1396             "/View/Time Display Format/Seconds Since Previous Packet");
1397         recent.gui_time_format = -1;
1398         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
1399         break;
1400     default:
1401         g_assert_not_reached();
1402     }
1403 }
1404
1405
1406 gint
1407 popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data)
1408 {
1409     GtkWidget *menu = (GtkWidget *)data;
1410     GdkEventButton *event_button = NULL;
1411     gint row, column;
1412
1413     if(widget == NULL || event == NULL || data == NULL) {
1414         return FALSE;
1415     }
1416
1417     /*
1418      * If we ever want to make the menu differ based on what row
1419      * and/or column we're above, we'd use "eth_clist_get_selection_info()"
1420      * to find the row and column number for the coordinates; a CTree is,
1421      * I guess, like a CList with one column(?) and the expander widget
1422      * as a pixmap.
1423      */
1424     /* Check if we are on packet_list object */
1425     if (widget == OBJECT_GET_DATA(popup_menu_object, E_MPACKET_LIST_KEY)) {
1426         if (packet_list_get_event_row_column(widget, (GdkEventButton *)event,
1427                                              &row, &column)) {
1428             OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_ROW_KEY,
1429                             GINT_TO_POINTER(row));
1430             OBJECT_SET_DATA(popup_menu_object, E_MPACKET_LIST_COL_KEY,
1431                             GINT_TO_POINTER(column));
1432             packet_list_set_selected_row(row);
1433         }
1434     }
1435
1436     /* Check if we are on tree_view object */
1437     if (widget == tree_view) {
1438         tree_view_select(widget, (GdkEventButton *) event);
1439     }
1440
1441     /* Check if we are on byte_view object */
1442     if(widget == get_notebook_bv_ptr(byte_nb_ptr)) {
1443         byte_view_select(widget, (GdkEventButton *) event);
1444     }
1445
1446     /* context menu handler (but the byte view notebook pages have their own handler) */
1447     if(event->type == GDK_BUTTON_PRESS && widget != byte_nb_ptr) {
1448         event_button = (GdkEventButton *) event;
1449
1450         /* To qoute the "Gdk Event Structures" doc:
1451          * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
1452         if(event_button->button == 3) {
1453             gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1454                            event_button->button,
1455                            event_button->time);
1456             SIGNAL_EMIT_STOP_BY_NAME(widget, "button_press_event");
1457             return TRUE;
1458         }
1459     }
1460 #if GTK_MAJOR_VERSION >= 2
1461     /* GDK_2BUTTON_PRESS is a doubleclick -> expand/collapse tree row */
1462     /* GTK version 1 seems to be doing this automatically */
1463     if (widget == tree_view && event->type == GDK_2BUTTON_PRESS) {
1464         GtkTreePath      *path;
1465
1466         if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
1467                                           (gint) (((GdkEventButton *)event)->x),
1468                                           (gint) (((GdkEventButton *)event)->y),
1469                                           &path, NULL, NULL, NULL))
1470         {
1471             if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path))
1472                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
1473             else
1474                 gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path,
1475                                          FALSE);
1476             gtk_tree_path_free(path);
1477         }
1478     }
1479 #endif
1480     return FALSE;
1481 }
1482
1483 /* Enable or disable menu items based on whether you have a capture file
1484    you've finished reading. */
1485 void
1486 set_menus_for_capture_file(gboolean have_capture_file)
1487 {
1488   set_menu_sensitivity(main_menu_factory, "/File/Open...", have_capture_file);
1489   set_menu_sensitivity(main_menu_factory, "/File/Open Recent", have_capture_file);
1490   set_menu_sensitivity(main_menu_factory, "/File/Merge...", have_capture_file);
1491   set_menu_sensitivity(main_menu_factory, "/File/Close", have_capture_file);
1492   set_menu_sensitivity(main_menu_factory, "/File/Save As...",
1493       have_capture_file);
1494   set_menu_sensitivity(main_menu_factory, "/File/Export", have_capture_file);
1495   set_menu_sensitivity(main_menu_factory, "/View/Reload", have_capture_file);
1496   set_toolbar_for_capture_file(have_capture_file);
1497   packets_bar_update();
1498 }
1499
1500 /* Enable or disable menu items based on whether you have an unsaved
1501    capture file you've finished reading. */
1502 void
1503 set_menus_for_unsaved_capture_file(gboolean have_unsaved_capture_file)
1504 {
1505   set_menu_sensitivity(main_menu_factory, "/File/Save",
1506       have_unsaved_capture_file);
1507   set_toolbar_for_unsaved_capture_file(have_unsaved_capture_file);
1508 }
1509
1510 /* Enable or disable menu items based on whether there's a capture in
1511    progress. */
1512 void
1513 set_menus_for_capture_in_progress(gboolean capture_in_progress)
1514 {
1515   set_menu_sensitivity(main_menu_factory, "/File/Open...",
1516       !capture_in_progress);
1517   set_menu_sensitivity(main_menu_factory, "/File/Open Recent", 
1518       !capture_in_progress);
1519 #ifdef HAVE_LIBPCAP
1520   set_menu_sensitivity(main_menu_factory, "/Capture/Start...",
1521       !capture_in_progress);
1522   set_menu_sensitivity(main_menu_factory, "/Capture/Stop",
1523       capture_in_progress);
1524   set_toolbar_for_capture_in_progress(capture_in_progress);
1525
1526   set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1527 #endif /* HAVE_LIBPCAP */
1528 }
1529
1530 /* Enable or disable menu items based on whether you have some captured
1531    packets. */
1532 static gboolean
1533 walk_menu_tree_for_captured_packets(GList *node,
1534     gboolean have_captured_packets)
1535 {
1536         gboolean    is_enabled;
1537         GList       *child;
1538         menu_item_t *node_data = node->data;
1539
1540         /*
1541          * Is this a leaf node or an interior node?
1542          */
1543         if (node_data->children == NULL) {
1544                 /*
1545                  * It's a leaf node.
1546                  *
1547                  * If it has no "selected_packet_enabled()" or
1548                  * "selected_tree_row_enabled()" routines, we enable
1549                  * it.  This allows tap windows to be popped up even
1550                  * if you have no capture file; this is done to let
1551                  * the user pop up multiple tap windows before reading
1552                  * in a capture file, so that they can be processed in
1553                  * parallel while the capture file is being read rather
1554                  * than one at at time as you pop up the windows, and to
1555                  * let the user pop up tap windows before starting an
1556                  * "Update list of packets in real time" capture, so that
1557                  * the statistics can be displayed while the capture is
1558                  * in progress.
1559                  *
1560                  * If it has either of those routines, we disable it for
1561                  * now - as long as, when a capture is first available,
1562                  * we don't get called after a packet or tree row is
1563                  * selected, that's OK.
1564                  * XXX - that should be done better.
1565                  */
1566                 if (node_data->selected_packet_enabled == NULL &&
1567                     node_data->selected_tree_row_enabled == NULL)
1568                         node_data->enabled = TRUE;
1569                 else
1570                         node_data->enabled = FALSE;
1571         } else {
1572                 /*
1573                  * It's an interior node; call
1574                  * "walk_menu_tree_for_captured_packets()" on all its
1575                  * children and, if any of them are enabled, enable
1576                  * this node, otherwise disable it.
1577                  *
1578                  * XXX - should we just leave all interior nodes enabled?
1579                  * Which is a better UI choice?
1580                  */
1581                 is_enabled = FALSE;
1582                 for (child = node_data->children; child != NULL; child =
1583                     child->next) {
1584                         if (walk_menu_tree_for_captured_packets(child,
1585                             have_captured_packets))
1586                                 is_enabled = TRUE;
1587                 }
1588                 node_data->enabled = is_enabled;
1589         }
1590
1591         /*
1592          * The root node doesn't correspond to a menu tree item; it
1593          * has a null name pointer.
1594          */
1595         if (node_data->name != NULL) {
1596                 set_menu_sensitivity(main_menu_factory, node_data->name,
1597                     node_data->enabled);
1598         }
1599         return node_data->enabled;
1600 }
1601
1602 void
1603 set_menus_for_captured_packets(gboolean have_captured_packets)
1604 {
1605   set_menu_sensitivity(main_menu_factory, "/File/Print...",
1606       have_captured_packets);
1607   set_menu_sensitivity(packet_list_menu_factory, "/Print...",
1608       have_captured_packets);
1609   set_menu_sensitivity(main_menu_factory, "/Edit/Find Packet...",
1610       have_captured_packets);
1611   set_menu_sensitivity(main_menu_factory, "/Edit/Find Next",
1612       have_captured_packets);
1613   set_menu_sensitivity(main_menu_factory, "/Edit/Find Previous",
1614       have_captured_packets);
1615   set_menu_sensitivity(main_menu_factory, "/View/Zoom In",
1616       have_captured_packets);
1617   set_menu_sensitivity(main_menu_factory, "/View/Zoom Out",
1618       have_captured_packets);
1619   set_menu_sensitivity(main_menu_factory, "/View/Normal Size",
1620       have_captured_packets);
1621   set_menu_sensitivity(main_menu_factory, "/Go/Go to Packet...",
1622       have_captured_packets);
1623   set_menu_sensitivity(main_menu_factory, "/Go/First Packet",
1624       have_captured_packets);
1625   set_menu_sensitivity(main_menu_factory, "/Go/Last Packet",
1626       have_captured_packets);
1627   set_menu_sensitivity(main_menu_factory, "/Statistics/Summary",
1628       have_captured_packets);
1629   set_menu_sensitivity(main_menu_factory, "/Statistics/Protocol Hierarchy", 
1630       have_captured_packets);
1631       
1632   walk_menu_tree_for_captured_packets(tap_menu_tree_root,
1633       have_captured_packets);
1634   set_toolbar_for_captured_packets(have_captured_packets);
1635   packets_bar_update();
1636 }
1637
1638 /* Enable or disable menu items based on whether a packet is selected and,
1639    if so, on the properties of the packet. */
1640 static gboolean
1641 walk_menu_tree_for_selected_packet(GList *node, frame_data *fd,
1642     epan_dissect_t *edt)
1643 {
1644         gboolean is_enabled;
1645         GList *child;
1646         menu_item_t *node_data = node->data;
1647
1648         /*
1649          * Is this a leaf node or an interior node?
1650          */
1651         if (node_data->children == NULL) {
1652                 /*
1653                  * It's a leaf node.
1654                  *
1655                  * If it has no "selected_packet_enabled()" routine,
1656                  * leave its enabled/disabled status alone - it
1657                  * doesn't depend on whether we have a packet selected
1658                  * or not or on the selected packet.
1659                  *
1660                  * If it has a "selected_packet_enabled()" routine,
1661                  * call it and set the item's enabled/disabled status
1662                  * based on its return value.
1663                  */
1664                 if (node_data->selected_packet_enabled != NULL)
1665                         node_data->enabled = node_data->selected_packet_enabled(fd, edt);
1666         } else {
1667                 /*
1668                  * It's an interior node; call
1669                  * "walk_menu_tree_for_selected_packet()" on all its
1670                  * children and, if any of them are enabled, enable
1671                  * this node, otherwise disable it.
1672                  *
1673                  * XXX - should we just leave all interior nodes enabled?
1674                  * Which is a better UI choice?
1675                  */
1676                 is_enabled = FALSE;
1677                 for (child = node_data->children; child != NULL; child =
1678                     child->next) {
1679                         if (walk_menu_tree_for_selected_packet(child, fd, edt))
1680                                 is_enabled = TRUE;
1681                 }
1682                 node_data->enabled = is_enabled;
1683         }
1684
1685         /*
1686          * The root node doesn't correspond to a menu tree item; it
1687          * has a null name pointer.
1688          */
1689         if (node_data->name != NULL) {
1690                 set_menu_sensitivity(main_menu_factory, node_data->name,
1691                     node_data->enabled);
1692         }
1693         return node_data->enabled;
1694 }
1695
1696 void
1697 set_menus_for_selected_packet(capture_file *cf)
1698 {
1699   set_menu_sensitivity(main_menu_factory, "/Edit/Mark Packet (toggle)",
1700       cf->current_frame != NULL);
1701   set_menu_sensitivity(packet_list_menu_factory, "/Mark Packet (toggle)",
1702       cf->current_frame != NULL);
1703   set_menu_sensitivity(main_menu_factory, "/Edit/Time Reference",
1704       cf->current_frame != NULL);
1705   set_menu_sensitivity(packet_list_menu_factory, "/Time Reference",
1706       cf->current_frame != NULL);
1707   set_menu_sensitivity(main_menu_factory, "/Edit/Mark All Packets",
1708       cf->current_frame != NULL);
1709   set_menu_sensitivity(main_menu_factory, "/Edit/Unmark All Packets",
1710       cf->current_frame != NULL);
1711   set_menu_sensitivity(main_menu_factory, "/View/Collapse All",
1712       cf->current_frame != NULL);
1713   set_menu_sensitivity(tree_view_menu_factory, "/Collapse All",
1714       cf->current_frame != NULL);
1715   set_menu_sensitivity(main_menu_factory, "/View/Expand All",
1716       cf->current_frame != NULL);
1717   set_menu_sensitivity(tree_view_menu_factory, "/Expand All",
1718       cf->current_frame != NULL);
1719   set_menu_sensitivity(main_menu_factory, "/View/Show Packet in New Window",
1720       cf->current_frame != NULL);
1721   set_menu_sensitivity(packet_list_menu_factory, "/Show Packet in New Window",
1722       cf->current_frame != NULL);
1723   set_menu_sensitivity(main_menu_factory, "/Analyze/Follow TCP Stream",
1724       cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
1725   set_menu_sensitivity(packet_list_menu_factory, "/Follow TCP Stream",
1726       cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
1727   set_menu_sensitivity(tree_view_menu_factory, "/Follow TCP Stream",
1728       cf->current_frame != NULL ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
1729   set_menu_sensitivity(main_menu_factory, "/Analyze/Decode As...",
1730       cf->current_frame != NULL && decode_as_ok());
1731   set_menu_sensitivity(packet_list_menu_factory, "/Decode As...",
1732       cf->current_frame != NULL && decode_as_ok());
1733   set_menu_sensitivity(tree_view_menu_factory, "/Decode As...",
1734       cf->current_frame != NULL && decode_as_ok());
1735   set_menu_sensitivity(main_menu_factory, "/View/Name Resolution/Resolve Name",
1736       cf->current_frame != NULL && (g_resolv_flags & RESOLV_ALL_ADDRS) != RESOLV_ALL_ADDRS);
1737   set_menu_sensitivity(tree_view_menu_factory, "/Resolve Name",
1738       cf->current_frame != NULL && (g_resolv_flags & RESOLV_ALL_ADDRS) != RESOLV_ALL_ADDRS);
1739   set_menu_sensitivity(packet_list_menu_factory, "/Apply as Filter",
1740       cf->current_frame != NULL);
1741   set_menu_sensitivity(packet_list_menu_factory, "/Prepare a Filter",
1742       cf->current_frame != NULL);
1743
1744   walk_menu_tree_for_selected_packet(tap_menu_tree_root, cf->current_frame,
1745       cf->edt);
1746   packets_bar_update();
1747 }
1748
1749 /* Enable or disable menu items based on whether a tree row is selected
1750    and, if so, on the properties of the tree row. */
1751 static gboolean
1752 walk_menu_tree_for_selected_tree_row(GList *node, field_info *fi)
1753 {
1754         gboolean is_enabled;
1755         GList *child;
1756         menu_item_t *node_data = node->data;
1757
1758         /*
1759          * Is this a leaf node or an interior node?
1760          */
1761         if (node_data->children == NULL) {
1762                 /*
1763                  * It's a leaf node.
1764                  *
1765                  * If it has no "selected_tree_row_enabled()" routine,
1766                  * leave its enabled/disabled status alone - it
1767                  * doesn't depend on whether we have a tree row selected
1768                  * or not or on the selected tree row.
1769                  *
1770                  * If it has a "selected_tree_row_enabled()" routine,
1771                  * call it and set the item's enabled/disabled status
1772                  * based on its return value.
1773                  */
1774                 if (node_data->selected_tree_row_enabled != NULL)
1775                         node_data->enabled = node_data->selected_tree_row_enabled(fi);
1776         } else {
1777                 /*
1778                  * It's an interior node; call
1779                  * "walk_menu_tree_for_selected_tree_row()" on all its
1780                  * children and, if any of them are enabled, enable
1781                  * this node, otherwise disable it.
1782                  *
1783                  * XXX - should we just leave all interior nodes enabled?
1784                  * Which is a better UI choice?
1785                  */
1786                 is_enabled = FALSE;
1787                 for (child = node_data->children; child != NULL; child =
1788                     child->next) {
1789                         if (walk_menu_tree_for_selected_tree_row(child, fi))
1790                                 is_enabled = TRUE;
1791                 }
1792                 node_data->enabled = is_enabled;
1793         }
1794
1795         /*
1796          * The root node doesn't correspond to a menu tree item; it
1797          * has a null name pointer.
1798          */
1799         if (node_data->name != NULL) {
1800                 set_menu_sensitivity(main_menu_factory, node_data->name,
1801                     node_data->enabled);
1802         }
1803         return node_data->enabled;
1804 }
1805
1806 void
1807 set_menus_for_selected_tree_row(capture_file *cf)
1808 {
1809   gboolean properties;
1810
1811
1812   set_menu_sensitivity(main_menu_factory, "/File/Export/Selected Packet Bytes...", 
1813       cf->finfo_selected != NULL);  
1814   set_menu_sensitivity(hexdump_menu_factory, "/Copy", 
1815       cf->finfo_selected != NULL);
1816   set_menu_sensitivity(hexdump_menu_factory, "/Export Selected Packet Bytes...", 
1817       cf->finfo_selected != NULL);
1818   
1819   if (cf->finfo_selected != NULL) {
1820         header_field_info *hfinfo = cf->finfo_selected->hfinfo;
1821         if (hfinfo->parent == -1) {
1822           properties = prefs_is_registered_protocol(hfinfo->abbrev);
1823         } else {
1824           properties = prefs_is_registered_protocol(proto_registrar_get_abbrev(hfinfo->parent));
1825         }
1826         set_menu_sensitivity(main_menu_factory,
1827           "/Go/Go to Corresponding Packet", hfinfo->type == FT_FRAMENUM);
1828         set_menu_sensitivity(tree_view_menu_factory,
1829           "/Go to Corresponding Packet", hfinfo->type == FT_FRAMENUM);
1830         set_menu_sensitivity(main_menu_factory, "/Analyze/Apply as Filter",
1831           proto_can_match_selected(cf->finfo_selected, cf->edt));
1832         set_menu_sensitivity(tree_view_menu_factory, "/Apply as Filter",
1833           proto_can_match_selected(cf->finfo_selected, cf->edt));
1834         set_menu_sensitivity(main_menu_factory, "/Analyze/Prepare a Filter",
1835           proto_can_match_selected(cf->finfo_selected, cf->edt));
1836         set_menu_sensitivity(tree_view_menu_factory, "/Prepare a Filter",
1837           proto_can_match_selected(cf->finfo_selected, cf->edt));
1838         set_menu_sensitivity(tree_view_menu_factory, "/Protocol Preferences...",
1839           properties);
1840         set_menu_sensitivity(main_menu_factory, "/View/Expand Tree", cf->finfo_selected->tree_type != -1);
1841 //      set_menu_sensitivity(tree_view_menu_factory, "/Expand Tree", cf->finfo_selected->tree_type != -1);
1842         set_menu_sensitivity(tree_view_menu_factory, "/Wiki Protocol Page",
1843           TRUE);
1844         set_menu_sensitivity(tree_view_menu_factory, "/Filter Field Reference",
1845           TRUE);
1846   } else {
1847         set_menu_sensitivity(main_menu_factory,
1848             "/Go/Go to Corresponding Packet", FALSE);
1849         set_menu_sensitivity(tree_view_menu_factory,
1850             "/Go to Corresponding Packet", FALSE);
1851         set_menu_sensitivity(main_menu_factory, "/Analyze/Apply as Filter", FALSE);
1852         set_menu_sensitivity(tree_view_menu_factory, "/Apply as Filter", FALSE);
1853         set_menu_sensitivity(main_menu_factory, "/Analyze/Prepare a Filter", FALSE);
1854         set_menu_sensitivity(tree_view_menu_factory, "/Prepare a Filter", FALSE);
1855         set_menu_sensitivity(tree_view_menu_factory, "/Protocol Preferences...",
1856           FALSE);
1857         set_menu_sensitivity(main_menu_factory, "/View/Expand Tree", FALSE);
1858         set_menu_sensitivity(tree_view_menu_factory, "/Wiki Protocol Page",
1859           FALSE);
1860         set_menu_sensitivity(tree_view_menu_factory, "/Filter Field Reference",
1861           FALSE);
1862   }
1863
1864   walk_menu_tree_for_selected_tree_row(tap_menu_tree_root, cf->finfo_selected);
1865 }
1866
1867 void set_menus_for_packet_history(gboolean back_history, gboolean forward_history) {
1868
1869   set_menu_sensitivity(main_menu_factory, "/Go/Back", back_history);
1870   set_menu_sensitivity(main_menu_factory, "/Go/Forward", forward_history);
1871
1872   set_toolbar_for_packet_history(back_history, forward_history);
1873 }
1874