changeset 1083:cbbbe0a96ecc

(last_nonmenu_event): New var. (syms_of_keyboard): New Lisp var. (read_key_sequence): Use that instead of prev_event. (read_char): Call read_char_menu_prompt here. Accept 4 new args to pass to it. Include them in recursive call. Don't delay before starting echo if prev_event was a mouse event. Test for eof in batch mode now understands C is a Lisp_Object. (read_key_sequence): Don't call it here; always call read_char. Don't change last_event_buffer after a mouse menu input. (read_char_menu_prompt): Arg PROMPT deleted. Return nil if nothing to do. (read_key_sequence): Keep track of prev_event. Pass new proper args to read_char_menu_prompt. (read_char_menu_prompt): New arg prev_event. Use Fx_popup_menu. Handle any number of keymaps, not just LOCAL and GLOBAL. Invert meaning of arg PROMPT. Test of menu_prompting was backwards. (keymap_table): No longer static.
author Richard M. Stallman <rms@gnu.org>
date Sat, 05 Sep 1992 00:09:33 +0000
parents d24afc1bef38
children a29670271217
files src/keyboard.c
diffstat 1 files changed, 242 insertions(+), 119 deletions(-) [+]
line wrap: on
line diff
--- a/src/keyboard.c	Sat Sep 05 00:08:07 1992 +0000
+++ b/src/keyboard.c	Sat Sep 05 00:09:33 1992 +0000
@@ -141,6 +141,10 @@
 /* Last input character read as a command.  */
 Lisp_Object last_command_char;
 
+/* Last input character read as a command, not counting menus
+   reached by the mouse.  */
+Lisp_Object last_nonmenu_event;
+
 /* Last input character read for any purpose.  */
 Lisp_Object last_input_char;
 
@@ -1052,9 +1056,22 @@
    -1 means do not do redisplay, but do do autosaving.
    1 means do both.  */
 
+/* The arguments MAPS and NMAPS are for menu prompting.
+   MAPS is an array of keymaps;  NMAPS is the length of MAPS.
+
+   PREV_EVENT is the previous input event, or nil if we are reading
+   the first event of a key sequence.
+
+   If we use a mouse menu to read the input, we store 1 into *USED_MOUSE_MENU.
+   Otherwise we store 0 there.  */
+
 Lisp_Object
-read_char (commandflag)
+read_char (commandflag, nmaps, maps, prev_event, used_mouse_menu)
      int commandflag;
+     int nmaps;
+     Lisp_Object *maps;
+     Lisp_Object prev_event;
+     int *used_mouse_menu;
 {
   register Lisp_Object c;
   int count;
@@ -1118,9 +1135,17 @@
     {
       Lisp_Object tem0;
 
-      tem0 = sit_for (echo_keystrokes, 0, 1, 1);
-      if (EQ (tem0, Qt))
+      /* After a mouse event, start echoing right away.
+	 This is because we are probably about to display a menu,
+	 and we don't want to delay before doing so.  */
+      if (XTYPE (prev_event) == Lisp_Cons)
 	echo ();
+      else
+	{
+	  tem0 = sit_for (echo_keystrokes, 0, 1, 1);
+	  if (EQ (tem0, Qt))
+	    echo ();
+	}
     }
 
   /* Maybe auto save due to number of keystrokes or idle time.  */
@@ -1136,55 +1161,65 @@
       restore_getcjmp (temp);
     }
 
+  /* Try reading a character via menu prompting.
+     Try this before the sit-for, because the sit-for
+     would do the wrong thing if we are supposed to do
+     menu prompting.  */
+  c = Qnil;
+  if (INTERACTIVE && !NILP (prev_event))
+    c = read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu);
+
   /* Slow down auto saves logarithmically in size of current buffer,
      and garbage collect while we're at it.  */
-  {
-    int delay_level, buffer_size;
-
-    if (! MINI_WINDOW_P (XWINDOW (selected_window)))
-      last_non_minibuf_size = Z - BEG;
-    buffer_size = (last_non_minibuf_size >> 8) + 1;
-    delay_level = 0;
-    while (buffer_size > 64)
-      delay_level++, buffer_size -= buffer_size >> 2;
-    if (delay_level < 4) delay_level = 4;
-    /* delay_level is 4 for files under around 50k, 7 at 100k,
-       9 at 200k, 11 at 300k, and 12 at 500k.  It is 15 at 1 meg.  */
-
-    /* Auto save if enough time goes by without input.  */
-    if (commandflag != 0
-	&& num_input_chars > last_auto_save
-	&& XTYPE (Vauto_save_timeout) == Lisp_Int
-	&& XINT (Vauto_save_timeout) > 0)
-      {
-	Lisp_Object tem0;
-	int delay = delay_level * XFASTINT (Vauto_save_timeout) / 4;
-	tem0 = sit_for (delay, 0, 1, 1);
-	if (EQ (tem0, Qt))
-	  {
-	    jmp_buf temp;
-	    save_getcjmp (temp);
-	    Fdo_auto_save (Qnil, Qnil);
-	    restore_getcjmp (temp);
-
-	    /* If we have auto-saved and there is still no input
-	       available, garbage collect if there has been enough
-	       consing going on to make it worthwhile.  */
-	    if (!detect_input_pending ()
-		&& consing_since_gc > gc_cons_threshold / 2)
-	      Fgarbage_collect ();
-	  }
-      }
-  }
+  if (NILP (c))
+    {
+      int delay_level, buffer_size;
+
+      if (! MINI_WINDOW_P (XWINDOW (selected_window)))
+	last_non_minibuf_size = Z - BEG;
+      buffer_size = (last_non_minibuf_size >> 8) + 1;
+      delay_level = 0;
+      while (buffer_size > 64)
+	delay_level++, buffer_size -= buffer_size >> 2;
+      if (delay_level < 4) delay_level = 4;
+      /* delay_level is 4 for files under around 50k, 7 at 100k,
+	 9 at 200k, 11 at 300k, and 12 at 500k.  It is 15 at 1 meg.  */
+
+      /* Auto save if enough time goes by without input.  */
+      if (commandflag != 0
+	  && num_input_chars > last_auto_save
+	  && XTYPE (Vauto_save_timeout) == Lisp_Int
+	  && XINT (Vauto_save_timeout) > 0)
+	{
+	  Lisp_Object tem0;
+	  int delay = delay_level * XFASTINT (Vauto_save_timeout) / 4;
+	  tem0 = sit_for (delay, 0, 1, 1);
+	  if (EQ (tem0, Qt))
+	    {
+	      jmp_buf temp;
+	      save_getcjmp (temp);
+	      Fdo_auto_save (Qnil, Qnil);
+	      restore_getcjmp (temp);
+
+	      /* If we have auto-saved and there is still no input
+		 available, garbage collect if there has been enough
+		 consing going on to make it worthwhile.  */
+	      if (!detect_input_pending ()
+		  && consing_since_gc > gc_cons_threshold / 2)
+		Fgarbage_collect ();
+	    }
+	}
+    }
 
   /* Actually read a character, waiting if necessary.  */
-  c = kbd_buffer_get_event ();
+  if (NILP (c))
+    c = kbd_buffer_get_event ();
 
   if (NILP (c))
     abort ();			/* Don't think this can happen. */
 
   /* Terminate Emacs in batch mode if at eof.  */
-  if (noninteractive && c < 0)
+  if (noninteractive && XTYPE (c) == Lisp_Int && XINT (c) < 0)
     Fkill_emacs (make_number (1));
 
  non_reread:
@@ -1278,7 +1313,7 @@
       if (EQ (c, make_number (040)))
 	{
 	  cancel_echoing ();
-	  c = read_char (0);
+	  c = read_char (0, 0, 0, Qnil, 0);
 	}
     }
 
@@ -1543,8 +1578,8 @@
 	kbd_fetch_ptr = kbd_buffer;
       /* Do the redirection specified by the focus_frame
 	 member now, before we return this event.  */
-      kbd_fetch_ptr->frame =
-	XFRAME (FRAME_FOCUS_FRAME (kbd_fetch_ptr->frame));
+      kbd_fetch_ptr->frame
+	= XFRAME (FRAME_FOCUS_FRAME (kbd_fetch_ptr->frame));
 
 #ifdef MULTI_FRAME
       XSET (Vlast_event_frame, Lisp_Frame, kbd_fetch_ptr->frame);
@@ -2233,54 +2268,85 @@
 static int echo_now;
 
 /* Read a character like read_char but optionally prompt based on maps
-   LOCAL and GLOBAL.
+   in the array MAPS.  NMAPS is the length of MAPS.
+
+   PREV_EVENT is the previous input event, or nil if we are reading
+   the first event of a key sequence.
+
+   If we use a mouse menu to read the input, we store 1 into *USED_MOUSE_MENU.
+   Otherwise we store 0 there.
 
    The prompting is done based on the prompt-string of the map
    and the strings associated with various map elements.  */
 
 Lisp_Object
-read_char_menu_prompt (prompt, local, global)
-     int prompt;
-     Lisp_Object local, global;
+read_char_menu_prompt (nmaps, maps, prev_event, used_mouse_menu)
+     int nmaps;
+     Lisp_Object *maps;
+     Lisp_Object prev_event;
+     int *used_mouse_menu;
 {
-  register Lisp_Object rest, name;
-  Lisp_Object hmap;
+  int mapno;
+  register Lisp_Object name;
   int nlength;
   int width = FRAME_WIDTH (selected_frame) - 4;
-  char *menu = (char *) alloca (width);
+  char *menu = (char *) alloca (width + 4);
+  int idx = -1;
+  Lisp_Object rest, vector;
+
+  *used_mouse_menu = 0;
 
   /* Use local over global Menu maps */
 
-  if (menu_prompting)
-    return read_char (!prompt);
-
-  /* We can't get prompt strings from dense keymaps.  */
-  if (CONSP (local)
-      && EQ (Fcar (local), Qkeymap)
-      && !(CONSP (XCONS (local)->cdr)
-	   && XTYPE (XCONS (XCONS (local)->cdr)->car) == Lisp_Vector))
-    hmap = local;
-  else if (CONSP (global)
-	   && EQ (Fcar (global), Qkeymap)
-	   && !(CONSP (XCONS (global)->cdr)
-		&& XTYPE (XCONS (XCONS (global)->cdr)->car) == Lisp_Vector))
-    hmap = global;
-  else
-    return read_char (!prompt);
-
-  /* Get the map's prompt string.  */
-  name = map_prompt (hmap);
+  if (! menu_prompting)
+    return Qnil;
+
+  /* Get the menu name from the first map that has one (a prompt string).  */
+  for (mapno = 0; mapno < nmaps; mapno++)
+    {
+      name = map_prompt (maps[mapno]);
+      if (!NILP (name))
+	break;
+    }
+
+  /* If we don't have any menus, just read a character normally.  */
   if (NILP (name))
-    return read_char (!prompt);
+    return Qnil;
+
+#ifdef HAVE_X_MENU
+  /* If we got to this point via a mouse click,
+     use a real menu for mouse selection.  */
+  if (XTYPE (prev_event) == Lisp_Cons)
+    {
+      /* Display the menu and get the selection.  */
+      Lisp_Object *realmaps
+	= (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
+      Lisp_Object value;
+      int nmaps1 = 0;
+
+      /* Use the maps that are not nil.  */
+      for (mapno = 0; mapno < nmaps; mapno++)
+	if (!NILP (maps[mapno]))
+	  realmaps[nmaps1++] = maps[mapno];
+
+      value = Fx_popup_menu (prev_event, Flist (nmaps1, realmaps));
+      if (NILP (value))
+	XSET (value, Lisp_Int, quit_char);
+      *used_mouse_menu = 1;
+      return value;
+    }
+#endif /* HAVE_X_MENU */
 
   /* Prompt string always starts with map's prompt, and a space.  */
   strcpy (menu, XSTRING (name)->data);
   nlength = XSTRING (name)->size;
+  menu[nlength++] = ':';
   menu[nlength++] = ' ';
   menu[nlength] = 0;
 
-  /* Start prompting at start of map.  */
-  rest = hmap;			/* Current menu item */
+  /* Start prompting at start of first map.  */
+  mapno = 0;
+  rest = maps[mapno];
 
   /* Present the documented bindings, a line at a time.  */
   while (1)
@@ -2290,54 +2356,92 @@
       Lisp_Object obj;
       int ch;
 
-      /* If reached end of map, start at beginning.  */
-      if (NILP (Fcdr (rest))) rest = hmap;
-
       /* Loop over elements of map.  */
-      while (!NILP (rest) && i < width)
+      while (i < width)
 	{
-	  Lisp_Object s;
-
-	  /* Look for conses whose cadrs are strings.  */
-	  s = Fcar_safe (Fcdr_safe (Fcar_safe (rest)));
-	  if (XTYPE (s) != Lisp_String)
-	    /* Ignore all other elements.  */
-	    ;
-	  /* If first such element, or enough room, add string to prompt.  */
-	  else if (XSTRING (s)->size + i < width
-		   || !notfirst)
+	  Lisp_Object s, elt;
+
+	  /* If reached end of map, start at beginning of next map.  */
+	  if (NILP (rest))
 	    {
-	      int thiswidth;
-
-	      /* Punctuate between strings.  */
-	      if (notfirst)
+	      mapno++;
+	      /* At end of last map, wrap around to first map if just starting,
+		 or end this line if already have something on it.  */
+	      if (mapno == nmaps)
 		{
-		  strcpy (menu + i, ", ");
-		  i += 2;
+		  if (notfirst)
+		    break;
+		  else
+		    mapno = 0;
 		}
-	      notfirst = 1;
-
-	      /* Add as much of string as fits.  */
-	      thiswidth = XSTRING (s)->size;
-	      if (thiswidth + i > width)
-		thiswidth = width - i;
-	      bcopy (XSTRING (s)->data, menu + i, thiswidth);
-	      i += thiswidth;
+	      rest = maps[mapno];
+	    }
+
+	  /* Look at the next element of the map.  */
+	  if (idx >= 0)
+	    elt = XVECTOR (vector)->contents[idx];
+	  else
+	    elt = Fcar_safe (rest);
+
+	  if (idx < 0 && XTYPE (elt) == Lisp_Vector)
+	    {
+	      /* If we found a dense table in the keymap,
+		 advanced past it, but start scanning its contents.  */
+	      rest = Fcdr_safe (rest);
+	      vector = elt;
+	      idx = 0;
 	    }
 	  else
 	    {
-	      /* If some elts don't fit, show there are more.  */
-	      strcpy (menu + i, "...");
-	      break;
+	      /* An ordinary element.  */
+	      s = Fcar_safe (Fcdr_safe (elt));
+	      if (XTYPE (s) != Lisp_String)
+		/* Ignore the element if it has no prompt string.  */
+		;
+	      /* If we have room for the prompt string, add it to this line.
+		 If this is the first on the line, always add it.  */
+	      else if (XSTRING (s)->size + i < width
+		       || !notfirst)
+		{
+		  int thiswidth;
+
+		  /* Punctuate between strings.  */
+		  if (notfirst)
+		    {
+		      strcpy (menu + i, ", ");
+		      i += 2;
+		    }
+		  notfirst = 1;
+
+		  /* Add as much of string as fits.  */
+		  thiswidth = XSTRING (s)->size;
+		  if (thiswidth + i > width)
+		    thiswidth = width - i;
+		  bcopy (XSTRING (s)->data, menu + i, thiswidth);
+		  i += thiswidth;
+		}
+	      else
+		{
+		  /* If this element does not fit, end the line now,
+		     and save the element for the next line.  */
+		  strcpy (menu + i, "...");
+		  break;
+		}
+
+	      /* Move past this element.  */
+	      if (idx >= 0 && idx + 1 >= XVECTOR (rest)->size)
+		/* Handle reaching end of dense table.  */
+		idx = -1;
+	      if (idx >= 0)
+		idx++;
+	      else
+		rest = Fcdr_safe (rest);
 	    }
-
-	  /* Move past this element.  */
-	  rest = Fcdr_safe (rest);
 	}
 
       /* Prompt with that and read response.  */
       message1 (menu);
-      obj = read_char (1);
+      obj = read_char (1, 0, 0, Qnil, 0);
 
       if (XTYPE (obj) != Lisp_Int)
 	return obj;
@@ -2350,7 +2454,6 @@
 	return obj;
     }
 }
-
 
 /* Reading key sequences.  */
 
@@ -2518,6 +2621,8 @@
   int fkey_start = 0, fkey_end = 0;
   Lisp_Object fkey_map = Vfunction_key_map;
 
+  last_nonmenu_event = Qnil;
+
   if (INTERACTIVE)
     {
       if (prompt)
@@ -2561,6 +2666,7 @@
 	 || (first_binding >= nmaps && fkey_start < t))
     {
       Lisp_Object key;
+      int used_mouse_menu = 0;
 
       if (t >= bufsize)
 	error ("key sequence too long");
@@ -2578,10 +2684,8 @@
 	{
 	  struct buffer *buf;
 
-	  if (!prompt && INTERACTIVE)
-	    key = read_char_menu_prompt (prompt, Qnil, Qnil);
-	  else
-	    key = read_char (!prompt);
+	  key = read_char (!prompt, nmaps, submaps, last_nonmenu_event,
+			   &used_mouse_menu);
 
 	  /* The above routines return -1 at the end of a macro.
 	     Emacs 18 handles this by returning immediately with a
@@ -2593,7 +2697,10 @@
 
 #ifdef MULTI_FRAME
 	  /* What buffer was this event typed/moused at?  */
-	  if (XTYPE (key) == Lisp_Int || XTYPE (key) == Lisp_Symbol)
+	  if (used_mouse_menu)
+	    /* Never change last_event_buffer for using a menu.  */
+	    buf = last_event_buffer;
+	  else if (XTYPE (key) == Lisp_Int || XTYPE (key) == Lisp_Symbol)
 	    buf = (XBUFFER
 		   (XWINDOW
 		    (FRAME_SELECTED_WINDOW
@@ -2638,6 +2745,12 @@
 				   submaps + first_binding)
 		       + first_binding);
       keybuf[t++] = key;
+      /* Normally, last_nonmenu_event gets the previous key we read.
+	 But when a mouse popup menu is being used,
+	 we don't update last_nonmenu_event; it continues to hold the mouse
+	 event that preceded the first level of menu.  */
+      if (!used_mouse_menu)
+	last_nonmenu_event = key;
 
       /* If the sequence is unbound, see if we can hang a function key
 	 off the end of it.  We only want to scan real keyboard input
@@ -3418,11 +3531,17 @@
 
   DEFVAR_LISP ("disabled-command-hook", &Vdisabled_command_hook,
     "Value is called instead of any command that is disabled\n\
-(has a non-nil `disabled' property).");
+\(has a non-nil `disabled' property).");
 
   DEFVAR_LISP ("last-command-char", &last_command_char,
     "Last terminal input key that was part of a command.");
 
+  DEFVAR_LISP ("last-nonmenu-event", &last_nonmenu_event,
+    "Last terminal input key in a command, except for mouse menus.\n\
+Mouse menus give back keys that don't look like mouse events;\n\
+this variable holds the actual mouse event that led to the menu,\n\
+so that you can determine whether the command was run by mouse or not.");
+
   DEFVAR_LISP ("last-input-char", &last_input_char,
     "Last terminal input key.");
 
@@ -3503,9 +3622,13 @@
   Vkeyboard_translate_table = Qnil;
 
   DEFVAR_BOOL ("menu-prompting", &menu_prompting,
-    "Non-nil means prompt with menus in echo area when appropriate.\n\
+    "Non-nil means prompt with menus when appropriate.\n\
 This is done when reading from a keymap that has a prompt string,\n\
-for elements that have prompt strings.");
+for elements that have prompt strings.\n\
+The menu is displayed on the screen\n\
+if X menus were enabled at configuration\n\
+time and the previous event was a mouse click prefix key.\n\
+Otherwise, menu prompting uses the echo area.");
   menu_prompting = 1;
 
   DEFVAR_LISP ("menu-prompt-more-char", &menu_prompt_more_char,