diff src/xmenu.c @ 49322:2cbb0b823e83

GTK version
author Jan Djärv <jan.h.d@swipnet.se>
date Sun, 19 Jan 2003 21:50:03 +0000
parents 17a9a82ad89f
children 23a1cea22d13
line wrap: on
line diff
--- a/src/xmenu.c	Sun Jan 19 20:03:42 2003 +0000
+++ b/src/xmenu.c	Sun Jan 19 21:50:03 2003 +0000
@@ -81,7 +81,9 @@
 #endif /* USE_LUCID */
 #include "../lwlib/lwlib.h"
 #else /* not USE_X_TOOLKIT */
+#ifndef USE_GTK
 #include "../oldXMenu/XMenu.h"
+#endif
 #endif /* not USE_X_TOOLKIT */
 #endif /* HAVE_X_WINDOWS */
 
@@ -116,6 +118,13 @@
 /* Define HAVE_BOXES if menus can handle radio and toggle buttons.  */
 
 #define HAVE_BOXES 1
+#endif /* USE_X_TOOLKIT */
+
+#ifdef USE_GTK
+#include "gtkutil.h"
+#define HAVE_BOXES 1
+extern void set_frame_menubar ();
+static Lisp_Object xdialog_show ();
 #endif
 
 static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
@@ -194,7 +203,7 @@
 
 /* Flag which when set indicates a dialog or menu has been posted by
    Xt on behalf of one of the widget sets.  */
-int popup_activated_flag;
+static int popup_activated_flag;
 
 static int next_menubar_widget_id;
 
@@ -593,7 +602,7 @@
   }
 #endif /* not HAVE_BOXES */
  
-#ifndef USE_X_TOOLKIT
+#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
   if (!NILP(map))
     /* Indicate visually that this is a submenu.  */
     item_string = concat2 (item_string, build_string (" >"));
@@ -606,7 +615,7 @@
 		  XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
 		  XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
 
-#ifdef USE_X_TOOLKIT
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
   /* Display a submenu using the toolkit.  */
   if (! (NILP (map) || NILP (enabled)))
     {
@@ -771,6 +780,7 @@
 #ifdef HAVE_MENUS
   if (! NILP (position))
     {
+      int get_current_pos_p = 0;
       check_x ();
 
       /* Decode the first argument: find the window and the coordinates.  */
@@ -778,6 +788,38 @@
 	  || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
 				   || EQ (XCAR (position), Qtool_bar))))
 	{
+          get_current_pos_p = 1;
+        }
+      else
+	{
+	  tem = Fcar (position);
+	  if (CONSP (tem))
+	    {
+	      window = Fcar (Fcdr (position));
+	      x = Fcar (tem);
+	      y = Fcar (Fcdr (tem));
+	    }
+	  else
+	    {
+	      for_click = 1;
+	      tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
+	      window = Fcar (tem);	     /* POSN_WINDOW (tem) */
+	      tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
+	      x = Fcar (tem);
+	      y = Fcdr (tem);
+	    }
+
+          /* If a click happens in an external tool bar or a detached
+             tool bar, x and y is NIL.  In that case, use the current
+             mouse position.  This happens for the help button in the
+             tool bar.  Ideally popup-menu should pass NIL to
+             this function, but it doesn't.  */
+          if (NILP (x) && NILP (y))
+            get_current_pos_p = 1;
+	}
+
+      if (get_current_pos_p)
+        {
 	  /* Use the mouse's current position.  */
 	  FRAME_PTR new_f = SELECTED_FRAME ();
 #ifdef HAVE_X_WINDOWS
@@ -813,25 +855,6 @@
 	      XSETFASTINT (y, 0);
 	    }
 	}
-      else
-	{
-	  tem = Fcar (position);
-	  if (CONSP (tem))
-	    {
-	      window = Fcar (Fcdr (position));
-	      x = Fcar (tem);
-	      y = Fcar (Fcdr (tem));
-	    }
-	  else
-	    {
-	      for_click = 1;
-	      tem = Fcar (Fcdr (position));  /* EVENT_START (position) */
-	      window = Fcar (tem);	     /* POSN_WINDOW (tem) */
-	      tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
-	      x = Fcar (tem);
-	      y = Fcdr (tem);
-	    }
-	}
 
       CHECK_NUMBER (x);
       CHECK_NUMBER (y);
@@ -1040,7 +1063,7 @@
        but I don't want to make one now.  */
     CHECK_WINDOW (window);
 
-#ifndef USE_X_TOOLKIT
+#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
   /* Display a menu with these alternatives
      in the middle of frame F.  */
   {
@@ -1081,7 +1104,7 @@
 #endif
 }
 
-#ifdef USE_X_TOOLKIT
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
 
 /* Loop in Xt until the menu pulldown or dialog popup has been
    popped down (deactivated).  This is used for x-popup-menu
@@ -1092,6 +1115,7 @@
    NOTE: All calls to popup_get_selection should be protected
    with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
 
+#ifdef USE_X_TOOLKIT
 static void
 popup_get_selection (initial_event, dpyinfo, id, do_timers)
      XEvent *initial_event;
@@ -1148,6 +1172,24 @@
     }
 }
 
+#endif /* USE_X_TOOLKIT */
+
+#ifdef USE_GTK
+/* Loop util popup_activated_flag is set to zero in a callback.
+   Used for popup menus and dialogs. */
+static void
+popup_widget_loop ()
+{
+  ++popup_activated_flag;
+
+  /* Process events in the Gtk event loop until done.  */
+  while (popup_activated_flag)
+    {
+      gtk_main_iteration ();
+    }
+}
+#endif
+
 /* Activate the menu bar of frame F.
    This is called from keyboard.c when it gets the
    MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
@@ -1169,9 +1211,20 @@
   if (!f->output_data.x->saved_menu_event->type)
     return;
 
+#ifdef USE_GTK  
+  if (! xg_win_to_widget (f->output_data.x->saved_menu_event->xany.window))
+    return;
+#endif
+  
   set_frame_menubar (f, 0, 1);
   BLOCK_INPUT;
+#ifdef USE_GTK
+  XPutBackEvent (f->output_data.x->display_info->display,
+                 f->output_data.x->saved_menu_event);
+  popup_activated_flag = 1;
+#else
   XtDispatchEvent (f->output_data.x->saved_menu_event);
+#endif
   UNBLOCK_INPUT;
 #ifdef USE_MOTIF
   if (f->output_data.x->saved_menu_event->type == ButtonRelease)
@@ -1193,6 +1246,7 @@
 /* This callback is invoked when the user selects a menubar cascade
    pushbutton, but before the pulldown menu is posted.  */
 
+#ifndef USE_GTK
 static void
 popup_activate_callback (widget, id, client_data)
      Widget widget;
@@ -1201,10 +1255,20 @@
 {
   popup_activated_flag = 1;
 }
+#endif
 
 /* This callback is invoked when a dialog or menu is finished being
    used and has been unposted.  */
 
+#ifdef USE_GTK
+static void
+popup_deactivate_callback (widget, client_data)
+     GtkWidget *widget;
+     gpointer client_data;
+{
+  popup_activated_flag = 0;
+}
+#else
 static void
 popup_deactivate_callback (widget, id, client_data)
      Widget widget;
@@ -1213,27 +1277,20 @@
 {
   popup_activated_flag = 0;
 }
-
-/* Lwlib callback called when menu items are highlighted/unhighlighted
-   while moving the mouse over them.  WIDGET is the menu bar or menu
-   popup widget.  ID is its LWLIB_ID.  CALL_DATA contains a pointer to
-   the widget_value structure for the menu item, or null in case of
-   unhighlighting.  */
-
-void
-menu_highlight_callback (widget, id, call_data)
-     Widget widget;
-     LWLIB_ID id;
-     void *call_data;
+#endif
+
+
+/* Function that finds the frame for WIDGET and shows the HELP text
+   for that widget.
+   F is the frame if known, or NULL if not known.  */
+static void
+show_help_event (f, widget, help)
+     FRAME_PTR f;
+     xt_or_gtk_widget widget;
+     Lisp_Object help;
 {
-  widget_value *wv = (widget_value *) call_data;
-  struct frame *f;
-  Lisp_Object frame, help;
-
-  help = wv ? wv->help : Qnil;
-  
-  /* Determine the frame for the help event.  */
-  f = menubar_id_to_frame (id);
+  Lisp_Object frame;
+
   if (f)
     {
       XSETFRAME (frame, f);
@@ -1243,7 +1300,7 @@
     {
       /* WIDGET is the popup menu.  It's parent is the frame's 
 	 widget.  See which frame that is.  */
-      Widget frame_widget = XtParent (widget);
+      xt_or_gtk_widget frame_widget = XtParent (widget);
       Lisp_Object tail;
 
       for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
@@ -1259,32 +1316,77 @@
     }
 }
 
-/* This callback is called from the menu bar pulldown menu
-   when the user makes a selection.
-   Figure out what the user chose
-   and put the appropriate events into the keyboard buffer.  */
-
-static void
-menubar_selection_callback (widget, id, client_data)
+/* Callback called when menu items are highlighted/unhighlighted
+   while moving the mouse over them.  WIDGET is the menu bar or menu
+   popup widget.  ID is its LWLIB_ID.  CALL_DATA contains a pointer to
+   the data structure for the menu item, or null in case of
+   unhighlighting.  */
+
+#ifdef USE_GTK
+void
+menu_highlight_callback (widget, call_data)
+     GtkWidget *widget;
+     gpointer call_data;
+{
+  xg_menu_item_cb_data *cb_data;
+  Lisp_Object help;
+  
+  cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (widget),
+                                                       XG_ITEM_DATA);
+  if (! cb_data) return;
+
+  help = call_data ? cb_data->help : Qnil;
+
+  /* If popup_activated_flag is greater than 1 we are in a popup menu.
+     Don't show help for them, they won't appear before the
+     popup is popped down.  */
+  if (popup_activated_flag <= 1)
+    show_help_event (cb_data->cl_data->f, widget, help);
+}
+#else
+void
+menu_highlight_callback (widget, id, call_data)
      Widget widget;
      LWLIB_ID id;
-     XtPointer client_data;
+     void *call_data;
+{
+  struct frame *f;
+  Lisp_Object help;
+
+  widget_value *wv = (widget_value *) call_data;
+
+  help = wv ? wv->help : Qnil;
+  
+  /* Determine the frame for the help event.  */
+  f = menubar_id_to_frame (id);
+
+  show_help_event (f, widget, help);
+}
+#endif
+
+/* Find the menu selection and store it in the keyboard buffer.
+   F is the frame the menu is on.
+   MENU_BAR_ITEMS_USED is the length of VECTOR.
+   VECTOR is an array of menu events for the whole menu.
+ */
+void
+find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
+     FRAME_PTR f;
+     int menu_bar_items_used;
+     Lisp_Object vector;
+     void *client_data;
 {
   Lisp_Object prefix, entry;
-  FRAME_PTR f = menubar_id_to_frame (id);
-  Lisp_Object vector;
   Lisp_Object *subprefix_stack;
   int submenu_depth = 0;
   int i;
 
-  if (!f)
-    return;
   entry = Qnil;
-  subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
-  vector = f->menu_bar_vector;
+  subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
   prefix = Qnil;
   i = 0;
-  while (i < f->menu_bar_items_used)
+
+  while (i < menu_bar_items_used)
     {
       if (EQ (XVECTOR (vector)->contents[i], Qnil))
 	{
@@ -1348,6 +1450,59 @@
     }
 }
 
+
+#ifdef USE_GTK
+/* Gtk calls callbacks just because we tell it what item should be
+   selected in a radio group.  If this variable is set to a non-zero
+   value, we are creating menus and don't want callbacks right now.
+*/
+static int xg_crazy_callback_abort;
+
+/* This callback is called from the menu bar pulldown menu
+   when the user makes a selection.
+   Figure out what the user chose
+   and put the appropriate events into the keyboard buffer.  */
+static void
+menubar_selection_callback (widget, client_data)
+     GtkWidget *widget;
+     gpointer client_data;
+{
+  xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
+
+  if (xg_crazy_callback_abort)
+    return;
+
+  if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
+    return;
+
+  find_and_call_menu_selection (cb_data->cl_data->f,
+                                cb_data->cl_data->menu_bar_items_used,
+                                cb_data->cl_data->menu_bar_vector,
+                                cb_data->call_data);
+}
+
+#else /* not USE_GTK */
+
+/* This callback is called from the menu bar pulldown menu
+   when the user makes a selection.
+   Figure out what the user chose
+   and put the appropriate events into the keyboard buffer.  */
+static void
+menubar_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  FRAME_PTR f;
+
+  f = menubar_id_to_frame (id);
+  if (!f)
+    return;
+  find_and_call_menu_selection (f, f->menu_bar_items_used,
+                                f->menu_bar_vector, client_data);
+}
+#endif /* not USE_GTK */
+
 /* Allocate a widget_value, blocking input.  */
 
 widget_value *
@@ -1623,9 +1778,12 @@
 update_frame_menubar (f)
      FRAME_PTR f;
 {
+#ifdef USE_GTK
+  return xg_update_frame_menubar (f);
+#else
   struct x_output *x = f->output_data.x;
   int columns, rows;
-
+  
   if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
     return 0;
 
@@ -1657,6 +1815,7 @@
   /* Force the pane widget to resize itself with the right values.  */
   EmacsFrameSetCharSize (x->edit_widget, columns, rows);
   UNBLOCK_INPUT;
+#endif
   return 1;
 }
 
@@ -1670,21 +1829,25 @@
      int first_time;
      int deep_p;
 {
-  Widget menubar_widget = f->output_data.x->menubar_widget;
+  xt_or_gtk_widget menubar_widget = f->output_data.x->menubar_widget;
+#ifdef USE_X_TOOLKIT
+  LWLIB_ID id;
+#endif
   Lisp_Object items;
   widget_value *wv, *first_wv, *prev_wv = 0;
   int i, last_i;
   int *submenu_start, *submenu_end;
   int *submenu_top_level_items, *submenu_n_panes;
 
-  LWLIB_ID id;
 
   XSETFRAME (Vmenu_updating_frame, f);
 
+#ifdef USE_X_TOOLKIT
   if (f->output_data.x->id == 0)
     f->output_data.x->id = next_menubar_widget_id++;
   id = f->output_data.x->id;
-
+#endif
+  
   if (! menubar_widget)
     deep_p = 1;
   else if (pending_menu_activation && !deep_p)
@@ -1893,6 +2056,35 @@
 
   BLOCK_INPUT;
 
+#ifdef USE_GTK
+  xg_crazy_callback_abort = 1;
+  if (menubar_widget)
+    {
+      /* The third arg is DEEP_P, which says to consider the entire
+	 menu trees we supply, rather than just the menu bar item names.  */
+      xg_modify_menubar_widgets (menubar_widget,
+                                 f,
+                                 first_wv,
+                                 deep_p,
+                                 G_CALLBACK (menubar_selection_callback),
+                                 G_CALLBACK (popup_deactivate_callback),
+                                 G_CALLBACK (menu_highlight_callback));
+    }
+  else
+    {
+      GtkWidget *wvbox = f->output_data.x->vbox_widget;
+      
+      menubar_widget
+        = xg_create_widget ("menubar", "menubar", f, first_wv, 
+                            G_CALLBACK (menubar_selection_callback),
+                            G_CALLBACK (popup_deactivate_callback),
+                            G_CALLBACK (menu_highlight_callback));
+
+      f->output_data.x->menubar_widget = menubar_widget;
+    }
+
+  
+#else /* not USE_GTK */
   if (menubar_widget)
     {
       /* Disable resizing (done for Motif!) */
@@ -1939,10 +2131,15 @@
 
     f->output_data.x->menubar_height = menubar_size;
   }
+#endif /* not USE_GTK */
   
   free_menubar_widget_value_tree (first_wv);
   update_frame_menubar (f);
 
+#ifdef USE_GTK
+  xg_crazy_callback_abort = 0;
+#endif
+
   UNBLOCK_INPUT;
 }
 
@@ -1963,8 +2160,10 @@
 
 
 /* Get rid of the menu bar of frame F, and free its storage.
-   This is used when deleting a frame, and when turning off the menu bar.  */
-
+   This is used when deleting a frame, and when turning off the menu bar.
+   For GTK this function is in gtkutil.c.  */
+
+#ifndef USE_GTK
 void
 free_frame_menubar (f)
      FRAME_PTR f;
@@ -2011,8 +2210,9 @@
       UNBLOCK_INPUT;
     }
 }
-
-#endif /* USE_X_TOOLKIT */
+#endif /* not USE_GTK */
+
+#endif /* USE_X_TOOLKIT || USE_GTK */
 
 /* xmenu_show actually displays a menu using the panes and items in menu_items
    and returns the value selected from it.
@@ -2030,7 +2230,116 @@
    ERROR is a place to store an error message string in case of failure.
    (We return nil on failure, but the value doesn't actually matter.)  */
 
-#ifdef USE_X_TOOLKIT
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+
+/* The item selected in the popup menu.  */
+static Lisp_Object *volatile menu_item_selection;
+
+#ifdef USE_GTK
+
+/* Used when position a popup menu.  See menu_position_func and
+   create_and_show_popup_menu below.  */
+struct next_popup_x_y
+{
+  int x;
+  int y;
+};
+
+/* The menu position function to use if we are not putting a popup
+   menu where the pointer is.
+   MENU is the menu to pop up.
+   X and Y shall on exit contain x/y where the menu shall pop up.
+   PUSH_IN is not documented in the GTK manual.
+   USER_DATA is any data passed in when calling gtk_menu_popup.
+   Here it points to a struct next_popup_x_y where the coordinates
+   to store in *X and *Y are.
+
+   Here only X and Y are used.  */
+static void
+menu_position_func (menu, x, y, push_in, user_data)
+     GtkMenu *menu;
+     gint *x;
+     gint *y;
+     gboolean *push_in;
+     gpointer user_data;
+{
+  *x = ((struct next_popup_x_y*)user_data)->x;
+  *y = ((struct next_popup_x_y*)user_data)->y;
+}
+
+static void
+popup_selection_callback (widget, client_data)
+     GtkWidget *widget;
+     gpointer client_data;
+{
+  xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
+
+  if (xg_crazy_callback_abort) return;
+  if (cb_data) menu_item_selection = (Lisp_Object *) cb_data->call_data;
+}
+
+/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
+   menu pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_popup_menu (f, first_wv, x, y, for_click)
+     FRAME_PTR f;
+     widget_value *first_wv;
+     int x;
+     int y;
+     int for_click;
+{
+  int i;
+  GtkWidget *menu;
+  GtkMenuPositionFunc pos_func = 0;  /* Pop up at pointer.  */
+  struct next_popup_x_y popup_x_y;
+
+  xg_crazy_callback_abort = 1;
+  menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
+                           G_CALLBACK (popup_selection_callback),
+                           G_CALLBACK (popup_deactivate_callback),
+                           G_CALLBACK (menu_highlight_callback));
+  xg_crazy_callback_abort = 0;
+  
+  for (i = 0; i < 5; i++)
+    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+      break;
+
+  if (! for_click)
+    {
+      /* Not invoked by a click.  pop up at x/y.  */
+      pos_func = menu_position_func;
+
+      /* Adjust coordinates to be root-window-relative.  */
+      x += f->output_data.x->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+      y += f->output_data.x->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+
+      popup_x_y.x = x;
+      popup_x_y.y = y;
+    }
+
+  /* Display the menu.  */
+  gtk_widget_show_all (menu);
+  gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
+  
+  xg_did_tearoff = 0;
+  /* Set this to one.  popup_widget_loop increases it by one, so it becomes
+     two.  show_help_echo uses this to detect popup menus.  */
+  popup_activated_flag = 1;
+  /* Process events that apply to the menu.  */
+  popup_widget_loop ();
+
+  if (xg_did_tearoff)
+    xg_keep_popup (menu, xg_did_tearoff);
+  else
+    gtk_widget_destroy (menu);
+    
+  /* Must reset this manually because the button release event is not passed
+     to Emacs event loop. */
+  FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
+}
+
+#else /* not USE_GTK */
 
 /* We need a unique id for each widget handled by the Lucid Widget
    library.
@@ -2042,8 +2351,6 @@
    next_menubar_widget_id.  */
 LWLIB_ID widget_id_tick;
 
-static Lisp_Object *volatile menu_item_selection;
-
 static void
 popup_selection_callback (widget, id, client_data)
      Widget widget;
@@ -2053,6 +2360,76 @@
   menu_item_selection = (Lisp_Object *) client_data;
 }
 
+/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
+   menu pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_popup_menu (f, first_wv, x, y, for_click)
+     FRAME_PTR f;
+     widget_value *first_wv;
+     int x;
+     int y;
+     int for_click;
+{
+  int i;
+  Arg av[2];
+  int ac = 0;
+  XButtonPressedEvent dummy;
+  LWLIB_ID menu_id;
+  Widget menu;
+  Window child;
+
+  menu_id = widget_id_tick++;
+  menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
+                           f->output_data.x->widget, 1, 0,
+                           popup_selection_callback,
+                           popup_deactivate_callback,
+                           menu_highlight_callback);
+
+  dummy.type = ButtonPress;
+  dummy.serial = 0;
+  dummy.send_event = 0;
+  dummy.display = FRAME_X_DISPLAY (f);
+  dummy.time = CurrentTime;
+  dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+  dummy.window = dummy.root;
+  dummy.subwindow = dummy.root;
+  dummy.x = x;
+  dummy.y = y;
+
+  /* Adjust coordinates to be root-window-relative.  */
+  x += f->output_data.x->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+  y += f->output_data.x->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+
+  dummy.x_root = x;
+  dummy.y_root = y;
+
+  dummy.state = 0;
+  dummy.button = 0;
+  for (i = 0; i < 5; i++)
+    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+      dummy.button = i;
+
+  /* Don't allow any geometry request from the user.  */
+  XtSetArg (av[ac], XtNgeometry, 0); ac++;
+  XtSetValues (menu, av, ac);
+
+  /* Display the menu.  */
+  lw_popup_menu (menu, (XEvent *) &dummy);
+  popup_activated_flag = 1;
+
+  /* Process events that apply to the menu.  */
+  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0);
+
+  /* fp turned off the following statement and wrote a comment
+     that it is unnecessary--that the menu has already disappeared.
+     Nowadays the menu disappears ok, all right, but
+     we need to delete the widgets or multiple ones will pile up.  */
+  lw_destroy_all_widgets (menu_id); 
+}
+
+#endif /* not USE_GTK */
+
 static Lisp_Object
 xmenu_show (f, x, y, for_click, keymaps, title, error)
      FRAME_PTR f;
@@ -2064,17 +2441,12 @@
      char **error;
 {
   int i;
-  LWLIB_ID menu_id;
-  Widget menu;
-  Arg av[2];
-  int ac = 0;
   widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
   widget_value **submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
   Lisp_Object *subprefix_stack
     = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
   int submenu_depth = 0;
-  XButtonPressedEvent dummy;
 
   int first_pane;
 
@@ -2266,71 +2638,15 @@
       first_wv->contents = wv_title;
     }
 
-  /* Actually create the menu.  */
-  menu_id = widget_id_tick++;
-  menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
-			   f->output_data.x->widget, 1, 0,
-			   popup_selection_callback,
-			   popup_deactivate_callback,
-			   menu_highlight_callback);
-  
-  /* See if whe positions are up to date.  Temporary code to be removed
-     when we are sure positions are always up to date.  */
-  {
-    int real_x, real_y;
-    x_real_positions (f, &real_x, &real_y);
-
-    if (real_x != f->output_data.x->left_pos ||
-        real_y != f->output_data.x->top_pos)
-      abort ();
-  }
-
-  dummy.type = ButtonPress;
-  dummy.serial = 0;
-  dummy.send_event = 0;
-  dummy.display = FRAME_X_DISPLAY (f);
-  dummy.time = CurrentTime;
-  dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
-  dummy.window = dummy.root;
-  dummy.subwindow = dummy.root;
-  dummy.x = x;
-  dummy.y = y;
-
-  /* Adjust coordinates to be root-window-relative.  */
-  x += f->output_data.x->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
-  y += f->output_data.x->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
-  
-  dummy.x_root = x;
-  dummy.y_root = y;
-  dummy.state = 0;
-  dummy.button = 0;
-  for (i = 0; i < 5; i++)
-    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
-      dummy.button = i;
-
-  /* Don't allow any geometry request from the user.  */
-  XtSetArg (av[ac], XtNgeometry, 0); ac++;
-  XtSetValues (menu, av, ac);
+  /* No selection has been chosen yet.  */
+  menu_item_selection = 0;
+
+  /* Actually create and show the menu until popped down.  */
+  create_and_show_popup_menu (f, first_wv, x, y, for_click);
 
   /* Free the widget_value objects we used to specify the contents.  */
   free_menubar_widget_value_tree (first_wv);
 
-  /* No selection has been chosen yet.  */
-  menu_item_selection = 0;
-
-  /* Display the menu.  */
-  lw_popup_menu (menu, (XEvent *) &dummy);
-  popup_activated_flag = 1;
-
-  /* Process events that apply to the menu.  */
-  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0);
-
-  /* fp turned off the following statement and wrote a comment
-     that it is unnecessary--that the menu has already disappeared.
-     Nowadays the menu disappears ok, all right, but
-     we need to delete the widgets or multiple ones will pile up.  */
-  lw_destroy_all_widgets (menu_id); 
-
   /* Find the selected item, and its pane, to return
      the proper value.  */
   if (menu_item_selection != 0)
@@ -2389,6 +2705,48 @@
   return Qnil;
 }
 
+#ifdef USE_GTK
+static void
+dialog_selection_callback (widget, client_data)
+     GtkWidget *widget;
+     gpointer client_data;
+{
+  /* The EMACS_INT cast avoids a warning.  There's no problem
+     as long as pointers have enough bits to hold small integers.  */
+  if ((int) (EMACS_INT) client_data != -1)
+    menu_item_selection = (Lisp_Object *) client_data;
+
+  popup_activated_flag = 0;
+}
+
+/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
+   dialog pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_dialog (f, first_wv)
+     FRAME_PTR f;
+     widget_value *first_wv;
+{
+  GtkWidget *menu;
+
+  menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
+                           G_CALLBACK (dialog_selection_callback),
+                           G_CALLBACK (popup_deactivate_callback),
+                           0);
+
+  if (menu)
+    {
+      /* Display the menu.  */
+      gtk_widget_show_all (menu);
+
+      /* Process events that apply to the menu.  */
+      popup_widget_loop ();
+  
+      gtk_widget_destroy (menu);
+    }
+}
+
+#else /* not USE_GTK */
 static void
 dialog_selection_callback (widget, id, client_data)
      Widget widget;
@@ -2399,12 +2757,14 @@
      as long as pointers have enough bits to hold small integers.  */
   if ((int) (EMACS_INT) client_data != -1)
     menu_item_selection = (Lisp_Object *) client_data;
+
   BLOCK_INPUT;
   lw_destroy_all_widgets (id);
   UNBLOCK_INPUT;
   popup_activated_flag = 0;
 }
 
+
 /* ARG is the LWLIB ID of the dialog box, represented
    as a Lisp object as (HIGHPART . LOWPART).  */
 
@@ -2421,6 +2781,46 @@
   return Qnil;
 }
 
+
+/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
+   dialog pops down.
+   menu_item_selection will be set to the selection.  */
+static void
+create_and_show_dialog (f, first_wv)
+     FRAME_PTR f;
+     widget_value *first_wv;
+{
+  LWLIB_ID dialog_id;
+
+  dialog_id = widget_id_tick++;
+  lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
+                    f->output_data.x->widget, 1, 0,
+                    dialog_selection_callback, 0, 0);
+  lw_modify_all_widgets (dialog_id, first_wv->contents, True);
+
+  /* Display the dialog box.  */
+  lw_pop_up_all_widgets (dialog_id);
+  popup_activated_flag = 1;
+
+  /* Process events that apply to the dialog box.
+     Also handle timers.  */
+  {
+    int count = SPECPDL_INDEX ();
+    int fact = 4 * sizeof (LWLIB_ID);
+    
+    /* xdialog_show_unwind is responsible for popping the dialog box down.  */
+    record_unwind_protect (xdialog_show_unwind,
+                           Fcons (make_number (dialog_id >> (fact)),
+                                  make_number (dialog_id & ~(-1 << (fact)))));
+
+    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1);
+
+    unbind_to (count, Qnil);
+  }
+}
+
+#endif /* not USE_GTK */
+
 static char * button_names [] = {
   "button1", "button2", "button3", "button4", "button5",
   "button6", "button7", "button8", "button9", "button10" };
@@ -2433,7 +2833,6 @@
      char **error;
 {
   int i, nb_buttons=0;
-  LWLIB_ID dialog_id;
   char dialog_name[6];
 
   widget_value *wv, *first_wv = 0, *prev_wv = 0;
@@ -2543,38 +2942,17 @@
     first_wv = wv;
   }
 
-  /* Actually create the dialog.  */
-  dialog_id = widget_id_tick++;
-  lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
-		    f->output_data.x->widget, 1, 0,
-		    dialog_selection_callback, 0, 0);
-  lw_modify_all_widgets (dialog_id, first_wv->contents, True);
-  /* Free the widget_value objects we used to specify the contents.  */
-  free_menubar_widget_value_tree (first_wv);
-
   /* No selection has been chosen yet.  */
   menu_item_selection = 0;
 
-  /* Display the dialog box.  */
-  lw_pop_up_all_widgets (dialog_id);
-  popup_activated_flag = 1;
-
-  /* Process events that apply to the dialog box.
-     Also handle timers.  */
-  {
-    int count = SPECPDL_INDEX ();
-
-    /* xdialog_show_unwind is responsible for popping the dialog box down.  */
-    record_unwind_protect (xdialog_show_unwind,
-			   Fcons (make_number (dialog_id >> (4 * sizeof (LWLIB_ID))),
-				  make_number (dialog_id & ~(-1 << (4 * sizeof (LWLIB_ID))))));
-
-    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1);
-
-    unbind_to (count, Qnil);
-  }
-
-  /* Find the selected item and pane, and return the corresponding value.  */
+  /* Actually create and show the dialog.  */
+  create_and_show_dialog (f, first_wv);
+
+  /* Free the widget_value objects we used to specify the contents.  */
+  free_menubar_widget_value_tree (first_wv);
+  
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
   if (menu_item_selection != 0)
     {
       Lisp_Object prefix;
@@ -2619,7 +2997,7 @@
   return Qnil;
 }
 
-#else /* not USE_X_TOOLKIT */
+#else /* not USE_X_TOOLKIT && not USE_GTK */
 
 /* The frame of the last activated non-toolkit menu bar.
    Used to generate menu help events.  */