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