changeset 6307:1f9fa4022502

(xdialog_show): New function to handle the display of dialog boxes. (Fx_popup_dialog): New function. (dialog_selection_callback): New function. (xmenu_show) [USE_X_TOOLKIT]: Don't call lw_destroy_all_widgets at the end. Do the work of construct_mouse_click in the ButtonRelease case.
author Fred Pierresteguy <F.Pierresteguy@frcl.bull.fr>
date Fri, 11 Mar 1994 18:01:00 +0000
parents 19fc40b33f52
children f34deea7dc2c
files src/xmenu.c
diffstat 1 files changed, 337 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/xmenu.c	Fri Mar 11 15:15:31 1994 +0000
+++ b/src/xmenu.c	Fri Mar 11 18:01:00 1994 +0000
@@ -94,6 +94,7 @@
 extern XtAppContext Xt_app_con;
 
 static int string_width ();
+static Lisp_Object xdialog_show ();
 #endif
 
 static Lisp_Object xmenu_show ();
@@ -826,6 +827,133 @@
   if (error_name) error (error_name);
   return selection;
 }
+
+DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 1, 2, 0,
+  "Pop up a dialog box and return user's selection.\n\
+POSITION is a position specification.  This is either a mouse button event\n\
+or a list ((XOFFSET YOFFSET) WINDOW)\n\
+where XOFFSET and YOFFSET are positions in characters from the top left\n\
+corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)\n\
+This controls the position of the center of the first line\n\
+in the first pane of the menu, not the top left of the menu as a whole.\n\
+If POSITION is t, it means to use the current mouse position.\n\
+\n\
+MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.\n\
+The menu items come from key bindings that have a menu string as well as\n\
+a definition; actually, the \"definition\" in such a key binding looks like\n\
+\(STRING . REAL-DEFINITION).  To give the menu a title, put a string into\n\
+the keymap as a top-level element.\n\n\
+You can also use a list of keymaps as MENU.\n\
+  Then each keymap makes a separate pane.\n\
+When MENU is a keymap or a list of keymaps, the return value\n\
+is a list of events.\n\n\
+Alternatively, you can specify a menu of multiple panes\n\
+  with a list of the form (TITLE PANE1 PANE2...),\n\
+where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
+Each ITEM is normally a cons cell (STRING . VALUE);\n\
+but a string can appear as an item--that makes a nonselectable line\n\
+in the menu.\n\
+With this form of menu, the return value is VALUE from the chosen item.\n\
+\n\
+If POSITION is nil, don't display the menu at all, just precalculate the\n\
+cached information about equivalent key sequences.")
+  (position, menu)
+     Lisp_Object position, menu;
+{
+  int number_of_panes, panes;
+  Lisp_Object keymap, tem;
+  int xpos, ypos;
+  Lisp_Object title;
+  char *error_name;
+  Lisp_Object selection;
+  int i, j;
+  FRAME_PTR f;
+  Lisp_Object x, y, window;
+  int keymaps = 0;
+  int menubarp = 0;
+  struct gcpro gcpro1;
+
+  check_x ();
+
+  if (! NILP (position))
+    {
+      /* Decode the first argument: find the window and the coordinates.  */
+      if (EQ (position, Qt))
+	{
+	  /* Use the mouse's current position.  */
+	  FRAME_PTR new_f;
+	  Lisp_Object bar_window;
+	  int part;
+	  unsigned long time;
+
+	  (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
+	  XSET (window, Lisp_Frame, new_f);
+	}
+
+      CHECK_NUMBER (x, 0);
+      CHECK_NUMBER (y, 0);
+
+      /* Decode where to put the menu.  */
+
+      if (XTYPE (window) == Lisp_Frame)
+	{
+	  f = XFRAME (window);
+
+	  xpos = 0;
+	  ypos = 0;
+	}
+      else if (XTYPE (window) == Lisp_Window)
+	{
+	  CHECK_LIVE_WINDOW (window, 0);
+	  f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
+
+	  xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
+	  ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
+	}
+      else
+	/* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
+	   but I don't want to make one now.  */
+	CHECK_WINDOW (window, 0);
+
+      xpos += XINT (x);
+      ypos += XINT (y);
+    }
+
+  title = Qnil;
+  GCPRO1 (title);
+
+  /* Decode the dialog items from what was specified.  */
+    {
+      /* We were given an old-fashioned menu.  */
+      title = Fcar (menu);
+      CHECK_STRING (title, 1);
+
+      list_of_panes (Fcdr (menu));
+
+      keymaps = 0;
+    }
+  
+  if (NILP (position))
+    {
+      discard_menu_items ();
+      UNGCPRO;
+      return Qnil;
+    }
+
+  /* Display them in a dialog box.  */
+  BLOCK_INPUT;
+
+  selection = xdialog_show (f, xpos, ypos, menubarp,
+			    keymaps, title, &error_name);
+  UNBLOCK_INPUT;
+
+  discard_menu_items ();
+
+  UNGCPRO;
+
+  if (error_name) error (error_name);
+  return selection;
+}
 
 #ifdef USE_X_TOOLKIT
 
@@ -939,6 +1067,19 @@
   UNBLOCK_INPUT;
 }
 
+static void
+dialog_selection_callback (widget, id, client_data)
+     Widget widget;
+     LWLIB_ID id;
+     XtPointer client_data;
+{
+  if ((int)client_data != -1)
+    menu_item_selection = (Lisp_Object *) client_data;
+  BLOCK_INPUT;
+  lw_destroy_all_widgets (id);
+  UNBLOCK_INPUT;
+}
+
 /* This recursively calls free_widget_value() on the tree of widgets.
    It must free all data that was malloc'ed for these widget_values.
    In Emacs, many slots are pointers into the data of Lisp_Strings, and
@@ -1186,6 +1327,9 @@
 
 #ifdef USE_X_TOOLKIT
 
+extern unsigned int x_mouse_grabbed;
+extern Lisp_Object Vmouse_depressed;
+
 static Lisp_Object
 xmenu_show (f, x, y, menubarp, keymaps, title, error)
      FRAME_PTR f;
@@ -1426,6 +1570,16 @@
       if (event.type == ButtonRelease)
 	{
 	  XtDispatchEvent (&event);
+	  if (! menubarp)
+	    {
+	      /* Do the work of construct_mouse_click since it can't
+		 be called. Initially, the popup menu has been called
+		 from a ButtonPress in the edit_widget. Then the mouse
+		 has been set to grabbed. Reset it now.  */
+	      x_mouse_grabbed &= ~(1 << event.xbutton.button);
+	      if (!x_mouse_grabbed)
+		Vmouse_depressed = Qnil;
+	    }
 	  break;
 	}
       else if (event.type == Expose)
@@ -1481,8 +1635,10 @@
       dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
     }
 
+#if 0 /* No need to do that. The menu has disappeared.  */
   /* Make sure the menu disappears.  */
   lw_destroy_all_widgets (menu_id); 
+#endif
 
   /* Unread any events that we got but did not handle.  */
   while (queue != NULL) 
@@ -1548,6 +1704,186 @@
   return Qnil;
 }
 
+static char * button_names [] = {
+  "button1", "button2", "button3", "button4", "button5",
+  "button6", "button7", "button8", "button9", "button10" };
+
+static Lisp_Object
+xdialog_show (f, x, y, menubarp, keymaps, title, error)
+     FRAME_PTR f;
+     int x;
+     int y;
+     int menubarp;
+     int keymaps;
+     Lisp_Object title;
+     char **error;
+{
+  int i, nb_buttons=0;
+  int dialog_id;
+  Widget menu;
+  XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
+
+  /* This is the menu bar item (if any) that led to this menu.  */
+  widget_value *menubar_item = 0;
+
+  widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+
+  /* Define a queue to save up for later unreading
+     all X events that don't pertain to the menu.  */
+  struct event_queue
+    {
+      XEvent event;
+      struct event_queue *next;
+    };
+  
+  struct event_queue *queue = NULL;
+  struct event_queue *queue_tmp;
+
+  *error = NULL;
+
+  /* Create a tree of widget_value objects
+     representing the text label and buttons.  */
+  {
+    Lisp_Object pane_name, prefix;
+    char *pane_string;
+    pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
+    prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
+    pane_string = (NILP (pane_name)
+		   ? "" : (char *) XSTRING (pane_name)->data);  
+    prev_wv = malloc_widget_value ();
+    prev_wv->value = pane_string;
+    if (keymaps && !NILP (prefix))
+      prev_wv->name++;
+    prev_wv->enabled = 1;
+    prev_wv->name = "message";
+    first_wv = prev_wv;
+ 
+    /* Loop over all panes and items, filling in the tree.  */
+    i = MENU_ITEMS_PANE_LENGTH;
+    while (i < menu_items_used)
+      {
+	
+	/* Create a new item within current pane.  */
+	Lisp_Object item_name, enable, descrip;
+	item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
+	enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
+	descrip
+	  = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
+	
+	wv = malloc_widget_value ();
+	prev_wv->next = wv;
+	wv->name = (char *) button_names [nb_buttons];
+	if (!NILP (descrip))
+	  wv->key = XSTRING (descrip)->data;
+	wv->value = XSTRING (item_name)->data;
+	wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
+	wv->enabled = !NILP (enable);
+	prev_wv = wv;
+
+	nb_buttons++;
+	i += MENU_ITEMS_ITEM_LENGTH;
+      }
+
+    wv = malloc_widget_value ();
+    wv->name = "Q2BR1";
+    wv->contents = first_wv;
+    first_wv = wv;
+
+  }
+
+  /* Actually create the dialog.  */
+  dialog_id = ++popup_id_tick;
+  menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
+			   f->display.x->widget, 1, 0,
+			   dialog_selection_callback, 0);
+  lw_modify_all_widgets (dialog_id, first_wv, True);
+  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 menu.  */
+  lw_pop_up_all_widgets (dialog_id);
+
+  /* Process events that apply to the menu.  */
+  while (1)
+    {
+      XEvent event;
+
+      XtAppNextEvent (Xt_app_con, &event);
+      if (event.type == ButtonRelease)
+	{
+	  XtDispatchEvent (&event);
+	  break;
+	}
+      else if (event.type == Expose)
+	process_expose_from_menu (event);
+      XtDispatchEvent (&event);
+      if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
+	{
+	  queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
+
+	  if (queue_tmp != NULL) 
+	    {
+	      queue_tmp->event = event;
+	      queue_tmp->next = queue;
+	      queue = queue_tmp;
+	    }
+	}
+    }
+ pop_down:
+
+  /* Unread any events that we got but did not handle.  */
+  while (queue != NULL) 
+    {
+      queue_tmp = queue;
+      XPutBackEvent (XDISPLAY &queue_tmp->event);
+      queue = queue_tmp->next;
+      free ((char *)queue_tmp);
+    }
+
+  /* Find the selected item, and its pane, to return
+     the proper value.  */
+  if (menu_item_selection != 0)
+    {
+      Lisp_Object prefix;
+
+      prefix = Qnil;
+      i = 0;
+      while (i < menu_items_used)
+	{
+	  Lisp_Object entry;
+
+	  if (EQ (XVECTOR (menu_items)->contents[i], Qt))
+	    {
+	      prefix
+		= XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
+	      i += MENU_ITEMS_PANE_LENGTH;
+	    }
+	  else
+	    {
+	      entry
+		= XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
+	      if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
+		{
+		  if (keymaps != 0)
+		    {
+		      entry = Fcons (entry, Qnil);
+		      if (!NILP (prefix))
+			entry = Fcons (prefix, entry);
+		    }
+		  return entry;
+		}
+	      i += MENU_ITEMS_ITEM_LENGTH;
+	    }
+	}
+    }
+
+  return Qnil;
+}
 #else /* not USE_X_TOOLKIT */
 
 static Lisp_Object
@@ -1761,4 +2097,5 @@
 
   popup_id_tick = (1<<16);	
   defsubr (&Sx_popup_menu);
+  defsubr (&Sx_popup_dialog);
 }