Include files from the "epan" directory and subdirectories thereof with
[obnox/wireshark/wip.git] / gtk / main.c
index 64e856136ef51a0098741a7a73db24d93479cb92..68d0fabbab76b509ecc7ea8555438801488b26ae 100644 (file)
@@ -1,6 +1,6 @@
 /* main.c
  *
- * $Id: main.c,v 1.205 2001/07/27 07:10:11 guy Exp $
+ * $Id: main.c,v 1.231 2002/01/21 07:37:41 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -46,6 +46,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #include <conio.h>
 #endif
 
-#include <epan.h>
+#include <epan/epan.h>
 #include <epan/filesystem.h>
+#include <epan/epan_dissect.h>
 
 #include "main.h"
-#include "timestamp.h"
-#include "packet.h"
+#include <epan/timestamp.h>
+#include <epan/packet.h>
 #include "capture.h"
 #include "summary.h"
 #include "file.h"
 #include "filters.h"
+#include "prefs.h"
 #include "menu.h"
 #include "../menu.h"
 #include "color.h"
 #include "color_utils.h"
 #include "filter_prefs.h"
-#include "prefs_dlg.h"
+#include "file_dlg.h"
 #include "column.h"
 #include "print.h"
-#include "resolv.h"
-#include "util.h"
+#include <epan/resolv.h>
+#ifdef HAVE_LIBPCAP
+#include "pcap-util.h"
+#endif
 #include "statusbar.h"
 #include "simple_dialog.h"
 #include "proto_draw.h"
-#include "dfilter/dfilter.h"
+#include <epan/dfilter/dfilter.h>
 #include "keys.h"
 #include "packet_win.h"
 #include "gtkglobals.h"
-#include "plugins.h"
+#include <epan/plugins.h>
 #include "colors.h"
-#include "strutil.h"
+#include <epan/strutil.h>
 #include "register.h"
-#include "prefs.h"
+#include "ringbuffer.h"
+#include "ui_util.h"
+#include "image/clist_ascend.xpm"
+#include "image/clist_descend.xpm"
 
 #ifdef WIN32
 #include "capture-wpcap.h"
 #endif
 
+typedef struct column_arrows {
+  GtkWidget *table;
+  GtkWidget *ascend_pm;
+  GtkWidget *descend_pm;
+} column_arrows;
 
-packet_info  pi;
 capture_file cfile;
 GtkWidget   *top_level, *packet_list, *tree_view, *byte_nb_ptr,
             *tv_scrollw, *pkt_scrollw;
@@ -159,6 +171,7 @@ static guint    main_ctx, file_ctx, help_ctx;
 static GString *comp_info_str;
 gchar       *ethereal_path = NULL;
 gchar       *last_open_dir = NULL;
+gint   root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height;
 
 ts_type timestamp_type = RELATIVE;
 
@@ -209,25 +222,310 @@ set_fonts(GdkFont *regular, GdkFont *bold)
 
 /* Match selected byte pattern */
 void
-match_selected_cb(GtkWidget *w, gpointer data)
+match_selected_cb_do(gpointer data, int action, gchar *text)
 {
-    char               *buf;
+    char               *ptr;
     GtkWidget          *filter_te;
 
-    filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
+    if (!text)
+       return;
+    g_assert(data);
+    filter_te = gtk_object_get_data(GTK_OBJECT(data), E_DFILTER_TE_KEY);
+    g_assert(filter_te);
+
+    ptr = gtk_editable_get_chars(GTK_EDITABLE(filter_te),0,-1);
 
-    buf = proto_alloc_dfilter_string(finfo_selected, cfile.pd);
+    switch (action&MATCH_SELECTED_MASK) {
+
+    case MATCH_SELECTED_REPLACE:
+       ptr = g_strdup(text);
+       break;
+
+    case MATCH_SELECTED_AND:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strdup(text);
+       else
+           ptr = g_strconcat("(", ptr, ") && (", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_OR:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strdup(text);
+       else
+           ptr = g_strconcat("(", ptr, ") || (", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_NOT:
+       ptr = g_strconcat("!(", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_AND_NOT:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strconcat("!(", text, ")", NULL);
+       else
+           ptr = g_strconcat("(", ptr, ") && !(", text, ")", NULL);
+       break;
+
+    case MATCH_SELECTED_OR_NOT:
+       if ((!ptr) || (0 == strlen(ptr)))
+           ptr = g_strconcat("!(", text, ")", NULL);
+       else
+           ptr = g_strconcat("(", ptr, ") || !(", text, ")", NULL);
+       break;
+
+    default:
+       break;
+    }
 
     /* create a new one and set the display filter entry accordingly */
-    gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
+    gtk_entry_set_text(GTK_ENTRY(filter_te), ptr);
 
     /* Run the display filter so it goes in effect. */
-    filter_packets(&cfile, buf);
+    if (action&MATCH_SELECTED_APPLY_NOW)
+       filter_packets(&cfile, ptr);
+
+    /* Don't g_free(ptr) here. filter_packets() will do it the next time
+       it's called. */
+    g_free(text);
+}
 
-    /* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
+void
+match_selected_cb_replace(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
 }
 
+void
+match_selected_cb_and(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
 
+void
+match_selected_cb_or(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_and_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+match_selected_cb_or_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_replace(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_REPLACE,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_and(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_or(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_and_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_AND_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+void
+prepare_selected_cb_or_not(GtkWidget *w, gpointer data)
+{
+    if (finfo_selected)
+       match_selected_cb_do((data ? data : w),
+           MATCH_SELECTED_OR_NOT,
+           proto_alloc_dfilter_string(finfo_selected, cfile.pd));
+}
+
+static gchar *
+get_text_from_packet_list(gpointer data)
+{
+    gint       row = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_ROW_KEY);
+    gint       column = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_COL_KEY);
+    frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(packet_list), row);
+    epan_dissect_t *edt;
+    gchar      *buf=NULL;
+    int         len;
+
+    if (fdata != NULL) {
+       wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
+                      cfile.pd, fdata->cap_len);
+
+       edt = epan_dissect_new(FALSE, FALSE);
+       epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata,
+                        &cfile.cinfo);
+       epan_dissect_fill_in_columns(edt);
+
+       if (strlen(cfile.cinfo.col_expr[column]) != 0 &&
+           strlen(cfile.cinfo.col_expr_val[column]) != 0) {
+           len = strlen(cfile.cinfo.col_expr[column]) +
+                 strlen(cfile.cinfo.col_expr_val[column]) + 5;
+           buf = g_malloc0(len);
+           snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column],
+                    cfile.cinfo.col_expr_val[column]);
+       }
+
+       epan_dissect_free(edt);
+    }
+           
+    return buf;
+}
+
+void
+match_selected_cb_replace2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_and2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_or2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_and_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+match_selected_cb_or_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_replace2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_REPLACE,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_and2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_or2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_NOT,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_and_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_AND_NOT,
+        get_text_from_packet_list(data));
+}
+
+void
+prepare_selected_cb_or_not2(GtkWidget *w, gpointer data)
+{
+    match_selected_cb_do(data,
+        MATCH_SELECTED_OR_NOT,
+        get_text_from_packet_list(data));
+}
 
 /* Run the current display filter on the current packet set, and
    redisplay. */
@@ -238,8 +536,10 @@ filter_activate_cb(GtkWidget *w, gpointer data)
   GList     *filter_list = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_FL_KEY);
   GList     *li, *nl = NULL;
   gboolean   add_filter = TRUE;
+  char *s = NULL;
   
-  char *s = gtk_entry_get_text(GTK_ENTRY(w));
+  g_assert(data);
+  s = gtk_entry_get_text(GTK_ENTRY(data));
   
   /* GtkCombos don't let us get at their list contents easily, so we maintain
      our own filter list, and feed it to gtk_combo_set_popdown_strings when
@@ -274,7 +574,6 @@ filter_reset_cb(GtkWidget *w, gpointer data)
   if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) {
     gtk_entry_set_text(GTK_ENTRY(filter_te), "");
   }
-
   filter_packets(&cfile, NULL);
 }
 
@@ -327,16 +626,31 @@ packet_list_compare(GtkCList *clist, gconstpointer  ptr1, gconstpointer  ptr2)
 static void 
 packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data)
 {
+  column_arrows *col_arrows = (column_arrows *) data;
+  int i;
+  
+  gtk_clist_freeze(clist);
+  
+  for (i = 0; i < cfile.cinfo.num_cols; i++) {
+    gtk_widget_hide(col_arrows[i].ascend_pm);
+    gtk_widget_hide(col_arrows[i].descend_pm);
+  }
+  
   if (column == clist->sort_column) {
-    if (clist->sort_type == GTK_SORT_ASCENDING)
+    if (clist->sort_type == GTK_SORT_ASCENDING) {
       clist->sort_type = GTK_SORT_DESCENDING;
-    else
+      gtk_widget_show(col_arrows[column].descend_pm);
+    } else {
       clist->sort_type = GTK_SORT_ASCENDING;
+      gtk_widget_show(col_arrows[column].ascend_pm);
+    }
   }
   else {
     clist->sort_type = GTK_SORT_ASCENDING;
+    gtk_widget_show(col_arrows[column].ascend_pm);
     gtk_clist_set_sort_column(clist, column);
   }
+  gtk_clist_thaw(clist);
 
   gtk_clist_sort(clist);
 }
@@ -346,15 +660,18 @@ static void
 set_frame_mark(gboolean set, frame_data *frame, gint row) {
   GdkColor fg, bg;
 
-  if (frame == NULL || row == -1) return;
-  frame->flags.marked = set;
+  if (row == -1)
+    return;
   if (set) {
+    mark_frame(&cfile, frame);
     color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg);
     color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg);
   } else {
+    unmark_frame(&cfile, frame);
     fg = BLACK;
     bg = WHITE;
   }
+  file_set_save_marked_sensitive();
   gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg);
   gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg);
 }
@@ -388,7 +705,6 @@ void mark_frame_cb(GtkWidget *w, gpointer data) {
 
 static void mark_all_frames(gboolean set) {
   frame_data *fdata;
-  if (cfile.plist == NULL) return;
   for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) {
     set_frame_mark(set,
                   fdata,
@@ -420,8 +736,6 @@ void unmark_all_frames_cb(GtkWidget *w, gpointer data) {
 static void
 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
 
-  blank_packetinfo();
-
 /* Remove the hex display tabbed pages */
   while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0)))
     gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0);
@@ -451,7 +765,8 @@ tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user
        finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
        if (!finfo) return;
 
-       set_notebook_page(  byte_nb_ptr, find_notebook_page( byte_nb_ptr, finfo->ds_name));
+       if (finfo->ds_name != NULL)
+               set_notebook_page(  byte_nb_ptr, find_notebook_page( byte_nb_ptr, finfo->ds_name));
 
         byte_view = gtk_object_get_data(GTK_OBJECT(byte_nb_ptr), E_BYTE_VIEW_TEXT_INFO_KEY);
         byte_data = gtk_object_get_data(GTK_OBJECT(byte_view), E_BYTE_VIEW_DATA_PTR_KEY);
@@ -528,79 +843,25 @@ tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer us
 }
 
 void collapse_all_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree)
-    collapse_all_tree(cfile.protocol_tree, tree_view);
+  if (cfile.edt->tree)
+    collapse_all_tree(cfile.edt->tree, tree_view);
 }
 
 void expand_all_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree)
-    expand_all_tree(cfile.protocol_tree, tree_view);
+  if (cfile.edt->tree)
+    expand_all_tree(cfile.edt->tree, tree_view);
 }
 
 void resolve_name_cb(GtkWidget *widget, gpointer data) {
-  if (cfile.protocol_tree) {
-    gint tmp = prefs.name_resolve;
-    prefs.name_resolve = PREFS_RESOLV_ALL;
+  if (cfile.edt->tree) {
+    guint32 tmp = g_resolv_flags;
+    g_resolv_flags = RESOLV_ALL;
     gtk_clist_clear ( GTK_CLIST(tree_view) );
-    proto_tree_draw(cfile.protocol_tree, tree_view);
-    prefs.name_resolve = tmp;
+    proto_tree_draw(cfile.edt->tree, tree_view);
+    g_resolv_flags = tmp;
   }
 }
 
-/* Set the scrollbar placement of a scrolled window based upon pos value:
-   0 = left, 1 = right */
-void
-set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
-{
-       if (pos) {
-               gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
-                               GTK_CORNER_TOP_LEFT);
-       } else {
-               gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
-                               GTK_CORNER_TOP_RIGHT);
-       }
-}
-
-/* List of all scrolled windows, so we can globally set the scrollbar
-   placement of them. */
-static GList *scrolled_windows;
-
-/* Add a scrolled window to the list of scrolled windows. */
-static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
-
-void
-remember_scrolled_window(GtkWidget *scrollw)
-{
-  scrolled_windows = g_list_append(scrolled_windows, scrollw);
-
-  /* Catch the "destroy" event on the widget, so that we remove it from
-     the list when it's destroyed. */
-  gtk_signal_connect(GTK_OBJECT(scrollw), "destroy",
-                    GTK_SIGNAL_FUNC(forget_scrolled_window), NULL);
-}
-
-/* Remove a scrolled window from the list of scrolled windows. */
-static void
-forget_scrolled_window(GtkWidget *scrollw, gpointer data)
-{
-  scrolled_windows = g_list_remove(scrolled_windows, scrollw);
-}
-
-static void
-set_scrollbar_placement_cb(gpointer data, gpointer user_data)
-{
-       set_scrollbar_placement_scrollw((GtkWidget *)data,
-           *(int *)user_data);
-}
-
-/* Set the scrollbar placement of all scrolled windows based on pos value:
-   0 = left, 1 = right */
-void
-set_scrollbar_placement_all(int pos)
-{
-       g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
-}
-
 /* Set the selection mode of the packet list window. */
 void
 set_plist_sel_browse(gboolean val)
@@ -704,16 +965,7 @@ statusbar_pop_field_msg(void)
 }
 
 static gboolean
-main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
-{
-       file_quit_cmd_cb(widget, data);
-
-       /* Say that the window should be deleted. */
-       return FALSE;
-}
-
-void
-file_quit_cmd_cb (GtkWidget *widget, gpointer data)
+do_quit(void)
 {
        /* XXX - should we check whether the capture file is an
           unsaved temporary file for a live capture and, if so,
@@ -722,9 +974,19 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
           box to forcibly quit if the user clicks "OK"?
 
           If so, note that this should be done in a subroutine that
-          returns TRUE if we do so, and FALSE otherwise, and that
-          "main_window_delete_event_cb()" should return its
-          return value. */
+          returns TRUE if we do so, and FALSE otherwise, and if it
+          returns TRUE we should return TRUE without nuking anything.
+
+          Note that, if we do that, we might also want to check if
+          an "Update list of packets in real time" capture is in
+          progress and, if so, ask whether they want to terminate
+          the capture and discard it, and return TRUE, before nuking
+          any child capture, if they say they don't want to do so. */
+
+#ifdef HAVE_LIBPCAP
+       /* Nuke any child capture in progress. */
+       kill_capture_child();
+#endif
 
        /* Are we in the middle of reading a capture? */
        if (cfile.state == FILE_READ_IN_PROGRESS) {
@@ -735,6 +997,10 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
                   will check for that and, if it sees that, will clean
                   up and quit. */
                cfile.state = FILE_READ_ABORTED;
+
+               /* Say that the window should *not* be deleted;
+                  that'll be done by the code that cleans up. */
+               return TRUE;
        } else {
                /* Close any capture file we have open; on some OSes, you
                   can't unlink a temporary capture file if you have it
@@ -761,7 +1027,38 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
                /* Exit by leaving the main loop, so that any quit functions
                   we registered get called. */
                gtk_main_quit();
+
+               /* Say that the window should be deleted. */
+               return FALSE;
+       }
+}
+
+static gboolean
+main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+       gint desk_x, desk_y;
+
+       /* Try to grab our geometry */
+       gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+       if (gdk_window_get_deskrelative_origin(top_level->window,
+                               &desk_x, &desk_y)) {
+               if (desk_x <= root_x && desk_y <= root_y) {
+                       root_x = desk_x;
+                       root_y = desk_y;
+               }
        }
+
+       /* XXX - Is this the "approved" method? */
+       gdk_window_get_size(top_level->window, &top_width, &top_height);
+
+       /* "do_quit()" indicates whether the main window should be deleted. */
+       return do_quit();
+}
+
+void
+file_quit_cmd_cb (GtkWidget *widget, gpointer data)
+{
+       do_quit();
 }
 
 static void 
@@ -770,20 +1067,22 @@ print_usage(void) {
   fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n",
          comp_info_str->str);
 #ifdef HAVE_LIBPCAP
-  fprintf(stderr, "%s [ -vh ] [ -klpQS ] [ -B <byte view height> ] [ -c <count> ]\n",
+  fprintf(stderr, "%s [ -vh ] [ -klpQS ] [ -a <capture autostop condition> ] ...\n",
          PACKAGE);
-  fprintf(stderr, "\t[ -f <capture filter> ] [ -i <interface> ] [ -m <medium font> ] \n");
-  fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
+  fprintf(stderr, "\t[ -b <number of ringbuffer files> ] [ -B <byte view height> ]\n");
+  fprintf(stderr, "\t[ -c <count> ] [ -f <capture filter> ] [ -i <interface> ]\n");
+  fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -N <resolving> ]\n");
   fprintf(stderr, "\t[ -o <preference setting> ] ... [ -P <packet list height> ]\n");
   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -s <snaplen> ] \n");
-  fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ] [ -w <savefile> ]\n");
+  fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
+  fprintf(stderr, "\t[ -w <savefile> ] [ <infile> ]\n");
 #else
   fprintf(stderr, "%s [ -vh ] [ -B <byte view height> ] [ -m <medium font> ]\n",
          PACKAGE);
   fprintf(stderr, "\t[ -n ] [ -N <resolving> ]\n");
   fprintf(stderr, "\t[ -o <preference setting> ... [ -P <packet list height> ]\n");
   fprintf(stderr, "\t[ -r <infile> ] [ -R <read filter> ] [ -t <time stamp format> ]\n");
-  fprintf(stderr, "\t[ -T <tree view height> ]\n");
+  fprintf(stderr, "\t[ -T <tree view height> ] [ <infile> ]\n");
 #endif
 }
 
@@ -797,7 +1096,7 @@ show_version(void)
   printf("%s %s, %s\n", PACKAGE, VERSION, comp_info_str->str);
 }
 
-int
+static int
 get_positive_int(const char *string, const char *name)
 {
   long number;
@@ -822,6 +1121,53 @@ get_positive_int(const char *string, const char *name)
   return number;
 }
 
+#ifdef HAVE_LIBPCAP
+/*
+ * Given a string of the form "<autostop criterion>:<value>", as might appear
+ * as an argument to a "-a" option, parse it and set the criterion in
+ * question.  Return an indication of whether it succeeded or failed
+ * in some fashion.
+ */
+static gboolean
+set_autostop_criterion(const char *autostoparg)
+{
+  u_char *p, *colonp;
+
+  colonp = strchr(autostoparg, ':');
+  if (colonp == NULL)
+    return FALSE;
+
+  p = colonp;
+  *p++ = '\0';
+
+  /*
+   * Skip over any white space (there probably won't be any, but
+   * as we allow it in the preferences file, we might as well
+   * allow it here).
+   */
+  while (isspace(*p))
+    p++;
+  if (*p == '\0') {
+    /*
+     * Put the colon back, so if our caller uses, in an
+     * error message, the string they passed us, the message
+     * looks correct.
+     */
+    *colonp = ':';
+    return FALSE;
+  }
+  if (strcmp(autostoparg,"duration") == 0) {
+    cfile.autostop_duration = get_positive_int(p,"autostop duration");
+  } else if (strcmp(autostoparg,"filesize") == 0) {
+    cfile.autostop_filesize = get_positive_int(p,"autostop filesize");
+  } else {
+    return FALSE;
+  }
+  *colonp = ':'; /* put the colon back */
+  return TRUE;
+}
+#endif
+
 /* And now our feature presentation... [ fade to music ] */
 int
 main(int argc, char *argv[])
@@ -844,7 +1190,8 @@ main(int argc, char *argv[])
   WSADATA             wsaData; 
 #endif
 
-  char                *gpf_path, *pf_path, *cf_path, *df_path;
+  char                *gpf_path, *cf_path, *df_path;
+  const char          *pf_path;
   int                  gpf_open_errno, pf_open_errno, cf_open_errno, df_open_errno;
   int                  err;
 #ifdef HAVE_LIBPCAP
@@ -864,6 +1211,9 @@ main(int argc, char *argv[])
   e_prefs             *prefs;
   char                 badopt;
   char                *bold_font_name;
+  gint                 desk_x, desk_y;
+  gboolean             prefs_write_needed = FALSE;
+
 
   ethereal_path = argv[0];
 
@@ -943,11 +1293,25 @@ main(int argc, char *argv[])
      to the "prefs.capture_prom_mode" setting in the preferences file;
      it should do what the parent process tells it to do, and if
      the parent process wants it not to run in promiscuous mode, it'll
-     tell it so with a "-p" flag. */
+     tell it so with a "-p" flag.
+
+     Otherwise, set promiscuous mode from the preferences setting. */
   if (capture_child)
-    prefs->capture_prom_mode = TRUE;
+    promisc_mode = TRUE;
+  else
+    promisc_mode = prefs->capture_prom_mode;
+
+  /* Set "Update list of packets in real time" mode from the preferences
+     setting. */
+  sync_mode = prefs->capture_real_time;
+
+  /* And do the same for "Automatic scrolling in live capture" mode. */
+  auto_scroll_live = prefs->capture_auto_scroll;
 #endif
 
+  /* Set the name resolution code's flags from the preferences. */
+  g_resolv_flags = prefs->name_resolve;
+
   /* Read the capture filter file. */
   read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
 
@@ -972,6 +1336,12 @@ main(int argc, char *argv[])
   cfile.save_file_fd   = -1;
   cfile.snap           = WTAP_MAX_PACKET_SIZE;
   cfile.count          = 0;
+#ifdef HAVE_LIBPCAP
+  cfile.autostop_duration = 0;
+  cfile.autostop_filesize = 0;
+  cfile.ringbuffer_on = FALSE;
+  cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+#endif
   col_init(&cfile.cinfo, prefs->num_cols);
 
   /* Assemble the compile-time options */
@@ -1037,8 +1407,28 @@ main(int argc, char *argv[])
 #endif
 
   /* Now get our args */
-  while ((opt = getopt(argc, argv, "B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:W:vZ:")) !=  EOF) {
+  while ((opt = getopt(argc, argv, "a:b:B:c:f:hi:klm:nN:o:pP:Qr:R:Ss:t:T:w:W:vZ:")) !=  EOF) {
     switch (opt) {
+      case 'a':        /* autostop criteria */
+#ifdef HAVE_LIBPCAP
+        if (set_autostop_criterion(optarg) == FALSE) {
+          fprintf(stderr, "ethereal: Invalid or unknown -a flag \"%s\"\n", optarg);
+          exit(1);          
+        }
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
+        break;
+      case 'b':        /* Ringbuffer option */
+#ifdef HAVE_LIBPCAP
+        cfile.ringbuffer_on = TRUE;
+        cfile.ringbuffer_num_files = get_positive_int(optarg, "number of ring buffer files");
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
+        break;
       case 'B':        /* Byte view pane height */
         bv_size = get_positive_int(optarg, "byte view pane height");
         break;
@@ -1081,7 +1471,12 @@ main(int argc, char *argv[])
 #endif
         break;
       case 'l':        /* Automatic scrolling in live capture mode */
-        prefs->capture_auto_scroll = TRUE;
+#ifdef HAVE_LIBPCAP
+        auto_scroll_live = TRUE;
+#else
+        capture_option_specified = TRUE;
+        arg_error = TRUE;
+#endif
         break;
       case 'm':        /* Fixed-width font for the display */
         if (prefs->gui_font_name != NULL)
@@ -1089,12 +1484,12 @@ main(int argc, char *argv[])
         prefs->gui_font_name = g_strdup(optarg);
         break;
       case 'n':        /* No name resolution */
-        prefs->name_resolve = PREFS_RESOLV_NONE;
+        g_resolv_flags = RESOLV_NONE;
         break;
       case 'N':        /* Select what types of addresses/port #s to resolve */
-        if (prefs->name_resolve == PREFS_RESOLV_ALL)
-          prefs->name_resolve = PREFS_RESOLV_NONE;
-        badopt = string_to_name_resolve(optarg, &prefs->name_resolve);
+        if (g_resolv_flags == RESOLV_ALL)
+          g_resolv_flags = RESOLV_NONE;
+        badopt = string_to_name_resolve(optarg, &g_resolv_flags);
         if (badopt != '\0') {
           fprintf(stderr, "ethereal: -N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'\n",
                        badopt);
@@ -1110,6 +1505,7 @@ main(int argc, char *argv[])
           break;
 
         case PREFS_SET_NO_SUCH_PREF:
+        case PREFS_SET_OBSOLETE:
           fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
                        optarg);
           exit(1);
@@ -1118,7 +1514,7 @@ main(int argc, char *argv[])
         break;
       case 'p':        /* Don't capture in promiscuous mode */
 #ifdef HAVE_LIBPCAP
-       prefs->capture_prom_mode = FALSE;
+       promisc_mode = FALSE;
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
@@ -1155,7 +1551,7 @@ main(int argc, char *argv[])
         break;
       case 'S':        /* "Sync" mode: used for following file ala tail -f */
 #ifdef HAVE_LIBPCAP
-        prefs->capture_real_time = TRUE;
+        sync_mode = TRUE;
 #else
         capture_option_specified = TRUE;
         arg_error = TRUE;
@@ -1228,6 +1624,61 @@ main(int argc, char *argv[])
         break;
     }
   }
+  argc -= optind;
+  argv += optind;
+  if (argc >= 1) {
+    if (cf_name != NULL) {
+      /*
+       * Input file name specified with "-r" *and* specified as a regular
+       * command-line argument.
+       */
+      arg_error = TRUE;
+    } else {
+      /*
+       * Input file name not specified with "-r", and a command-line argument
+       * was specified; treat it as the input file name.
+       *
+       * Yes, this is different from tethereal, where non-flag command-line
+       * arguments are a filter, but this works better on GUI desktops
+       * where a command can be specified to be run to open a particular
+       * file - yes, you could have "-r" as the last part of the command,
+       * but that's a bit ugly.
+       */
+      cf_name = g_strdup(argv[0]);
+    }
+    argc--;
+    argv++;
+  }
+
+  if (argc != 0) {
+    /*
+     * Extra command line arguments were specified; complain.
+     */
+    arg_error = TRUE;
+  }
+
+#ifdef HAVE_LIBPCAP
+  if (cfile.ringbuffer_on) {
+    /* Ring buffer works only under certain conditions:
+       a) ring buffer does not work with temporary files;
+       b) sync_mode and cfile.ringbuffer_on are mutually exclusive -
+          sync_mode takes precedence;
+       c) it makes no sense to enable the ring buffer if the maximum
+          file size is set to "infinite". */
+    if (cfile.save_file == NULL) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but capture isn't being saved to a permanent file.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+    if (sync_mode) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+    if (cfile.autostop_filesize == 0) {
+      fprintf(stderr, "ethereal: Ring buffer requested, but no maximum capture file size was specified.\n");
+      cfile.ringbuffer_on = FALSE;
+    }
+  }
+#endif
 
 #ifdef WIN32
   /* Load wpcap if possible */
@@ -1295,15 +1746,24 @@ main(int argc, char *argv[])
       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
     else
       cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+    cfile.cinfo.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
+    cfile.cinfo.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
   }
 
+#ifdef HAVE_LIBPCAP
   if (cfile.snap < 1)
     cfile.snap = WTAP_MAX_PACKET_SIZE;
   else if (cfile.snap < MIN_PACKET_SIZE)
     cfile.snap = MIN_PACKET_SIZE;
   
-  rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
-  sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
+  /* Check the value range of the ringbuffer_num_files parameter */
+  if (cfile.ringbuffer_num_files < RINGBUFFER_MIN_NUM_FILES)
+    cfile.ringbuffer_num_files = RINGBUFFER_MIN_NUM_FILES;
+  else if (cfile.ringbuffer_num_files > RINGBUFFER_MAX_NUM_FILES)
+    cfile.ringbuffer_num_files = RINGBUFFER_MAX_NUM_FILES;
+#endif
+  
+  rc_file = get_persconffile_path(RC_FILE, FALSE);
   gtk_rc_parse(rc_file);
 
   /* Try to load the regular and boldface fixed-width fonts */
@@ -1476,6 +1936,66 @@ main(int argc, char *argv[])
 
   gtk_main();
 
+       /* Try to save our geometry.  GTK+ provides two routines to get a
+                window's position relative to the X root window.  If I understand the
+                documentation correctly, gdk_window_get_deskrelative_origin applies
+                mainly to Enlightenment and gdk_window_get_root_origin applies for
+                all other WMs.
+          
+          The code below tries both routines, and picks the one that returns
+          the upper-left-most coordinates.
+          
+          More info at:
+
+          http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
+          http://www.gtk.org/faq/#AEN600 */
+
+       /* Re-read our saved preferences. */
+       /* XXX - Move all of this into a separate function? */
+       prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
+
+       if (pf_path == NULL) {
+               if (prefs->gui_geometry_save_position) {
+                       if (top_level->window != NULL) {
+                               gdk_window_get_root_origin(top_level->window, &root_x, &root_y);
+                               if (gdk_window_get_deskrelative_origin(top_level->window,
+                                                       &desk_x, &desk_y)) {
+                                       if (desk_x <= root_x && desk_y <= root_y) {
+                                               root_x = desk_x;
+                                               root_y = desk_y;
+                                       }
+                               }
+                       }
+                       if (prefs->gui_geometry_main_x != root_x) {
+                               prefs->gui_geometry_main_x = root_x;
+                               prefs_write_needed = TRUE;
+                       }
+                       if (prefs->gui_geometry_main_y != root_y) {
+                               prefs->gui_geometry_main_y = root_y;
+                               prefs_write_needed = TRUE;
+                       }
+               }
+               
+               if (prefs->gui_geometry_save_size) {
+                       if (top_level->window != NULL) {
+                               /* XXX - Is this the "approved" method? */
+                               gdk_window_get_size(top_level->window, &top_width, &top_height);
+                       }
+                       if (prefs->gui_geometry_main_width != top_width) {
+                               prefs->gui_geometry_main_width = top_width;
+                               prefs_write_needed = TRUE;
+                       }
+                       if (prefs->gui_geometry_main_height != top_height) {
+                               prefs->gui_geometry_main_height = top_height;
+                               prefs_write_needed = TRUE;
+                       }
+               }
+               
+               if (prefs_write_needed) {
+                       write_prefs(&pf_path);
+               }
+       }
+       
   epan_cleanup();
   g_free(rc_file);
 
@@ -1641,11 +2161,16 @@ static void
 create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
 {
   GtkWidget           *main_vbox, *menubar, *u_pane, *l_pane,
-                      *stat_hbox,
+                      *stat_hbox, *column_lb,
                       *filter_bt, *filter_cm, *filter_te,
+                      *filter_apply,
                       *filter_reset;
   GList               *filter_list = NULL;
   GtkAccelGroup       *accel;
+  GtkStyle            *win_style;
+  GdkBitmap           *ascend_bm, *descend_bm;
+  GdkPixmap           *ascend_pm, *descend_pm;
+  column_arrows       *col_arrows;
   int                  i;
   /* Display filter construct dialog has an Apply button, and "OK" not
      only sets our text widget, it activates it (i.e., it causes us to
@@ -1655,14 +2180,25 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
        TRUE,
        TRUE
   };
-
+  
   /* Main window */  
   top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_name(top_level, "main window");
   gtk_signal_connect(GTK_OBJECT(top_level), "delete_event", 
     GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
+  gtk_signal_connect (GTK_OBJECT (top_level), "realize",
+    GTK_SIGNAL_FUNC (window_icon_realize_cb), NULL);
   gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
-  gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
+  if (prefs->gui_geometry_save_position) {
+    gtk_widget_set_uposition(GTK_WIDGET(top_level),
+      prefs->gui_geometry_main_x, prefs->gui_geometry_main_y);
+  }
+  if (prefs->gui_geometry_save_size) {
+    gtk_widget_set_usize(GTK_WIDGET(top_level),
+      prefs->gui_geometry_main_width, prefs->gui_geometry_main_height);
+  } else {
+    gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
+  }
   gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
 
   /* Container for menu bar, paned windows and progress/info box */
@@ -1688,22 +2224,23 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_widget_show(u_pane);
 
   /* Packet list */
-  pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
+  pkt_scrollw = scrolled_window_new(NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-  set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
-  remember_scrolled_window(pkt_scrollw);
   gtk_widget_show(pkt_scrollw);
   gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
 
-  packet_list = gtk_clist_new_with_titles(cfile.cinfo.num_cols, cfile.cinfo.col_title);
+  packet_list = gtk_clist_new(cfile.cinfo.num_cols);
+  /* Column titles are filled in below */
   gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
+
+  col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * cfile.cinfo.num_cols);
   
   set_plist_sel_browse(prefs->gui_plist_sel_browse);
   set_plist_font(m_r_font);
   gtk_widget_set_name(packet_list, "packet list");
   gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
-    GTK_SIGNAL_FUNC(packet_list_click_column_cb), NULL);
+    GTK_SIGNAL_FUNC(packet_list_click_column_cb), col_arrows);
   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
@@ -1771,24 +2308,45 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
   gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
-    GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
+    GTK_SIGNAL_FUNC(filter_activate_cb), filter_te);
   gtk_widget_show(filter_cm);
 
   filter_reset = gtk_button_new_with_label("Reset");
   gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
   gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
-                    GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
+                    GTK_SIGNAL_FUNC(filter_reset_cb), NULL);
   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
   gtk_widget_show(filter_reset);
 
+  filter_apply = gtk_button_new_with_label("Apply");
+  gtk_object_set_data(GTK_OBJECT(filter_apply), E_DFILTER_CM_KEY, filter_cm);
+  gtk_object_set_data(GTK_OBJECT(filter_apply), E_DFILTER_FL_KEY, filter_list);
+  gtk_signal_connect(GTK_OBJECT(filter_apply), "clicked",
+                     GTK_SIGNAL_FUNC(filter_activate_cb), filter_te);
+  gtk_box_pack_start(GTK_BOX(stat_hbox), filter_apply, FALSE, TRUE, 1);
+  gtk_widget_show(filter_apply);
+
   /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
    * of any widget that ends up calling a callback which needs
    * that text entry pointer */
   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
-  set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/And Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Or Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/And Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Match/Or Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/And Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Or Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/And Not Selected", E_DFILTER_TE_KEY, filter_te);
+  set_menu_object_data("/Display/Prepare/Or Not Selected", E_DFILTER_TE_KEY, filter_te);
+  gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_DFILTER_TE_KEY, filter_te);
+  gtk_object_set_data(GTK_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packet_list);
 
   info_bar = gtk_statusbar_new();
   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
@@ -1799,6 +2357,34 @@ create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
   gtk_widget_show(info_bar);
 
   gtk_widget_show(top_level);
+
+  /* Fill in column titles.  This must be done after the top level window
+     is displayed. */
+  win_style = gtk_widget_get_style(top_level);
+  ascend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &ascend_bm,
+       &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_ascend_xpm);
+  descend_pm = gdk_pixmap_create_from_xpm_d(top_level->window, &descend_bm,
+       &win_style->bg[GTK_STATE_NORMAL], (gchar **)clist_descend_xpm);
+  for (i = 0; i < cfile.cinfo.num_cols; i++) {
+    col_arrows[i].table = gtk_table_new(2, 2, FALSE);
+    gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
+    column_lb = gtk_label_new(cfile.cinfo.col_title[i]);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2,
+       GTK_SHRINK, GTK_SHRINK, 0, 0);
+    gtk_widget_show(column_lb);
+    col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 
+       1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
+    if (i == 0) {
+      gtk_widget_show(col_arrows[i].ascend_pm);
+    }
+    col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
+    gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm,
+       1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+    gtk_clist_set_column_widget(GTK_CLIST(packet_list), i, col_arrows[i].table);
+    gtk_widget_show(col_arrows[i].table);
+  }
+  gtk_clist_column_titles_show(GTK_CLIST(packet_list));
 }