diff src/xmenu.c @ 12661:3c0fbefb3833

Include buffer.h. (x_activate_menubar): New function. (set_frame_menubar): New arg deep_p. Callers changed. Run various hooks here when deep_p is true. (frame_vector): Variable deleted. (syms_of_xmenu): Don't staticpro it. (frame_vector_add_frame): Function deleted. (menubar_id_to_frame): New function. (menubar_selection_callback): Use menubar_id_to_frame. (next_menubar_widget_id): New variable. (set_frame_menubar): Use next_menubar_widget_id. (free_frame_menubar): Get id from f->display.x->id.
author Richard M. Stallman <rms@gnu.org>
date Tue, 25 Jul 1995 20:26:10 +0000
parents eec80ea06395
children a3466dad7d6d
line wrap: on
line diff
--- a/src/xmenu.c	Tue Jul 25 20:24:46 1995 +0000
+++ b/src/xmenu.c	Tue Jul 25 20:26:10 1995 +0000
@@ -41,6 +41,7 @@
 #include "keyboard.h"
 #include "blockinput.h"
 #include "puresize.h"
+#include "buffer.h"
 
 #ifdef MSDOS
 #include "msdos.h"
@@ -90,7 +91,15 @@
 
 extern Lisp_Object Vdefine_key_rebound_commands;
 
+extern Lisp_Object Voverriding_local_map;
+extern Lisp_Object Voverriding_local_map_menu_flag;
+
+extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
+
+extern Lisp_Object Qmenu_bar_update_hook;
+
 #ifdef USE_X_TOOLKIT
+extern void set_frame_menubar ();
 extern void process_expose_from_menu ();
 extern XtAppContext Xt_app_con;
 
@@ -156,48 +165,34 @@
    Xt on behalf of one of the widget sets.  */
 static int popup_activated_flag;
 
-/* This holds a Lisp vector
-   which contains frames that have menu bars.
-   Each frame that has a menu bar is found at some index in this vector
-   and the menu bar widget refers to the frame through that index.  */
-static Lisp_Object frame_vector;
+static int next_menubar_widget_id;
 
-/* Return the index of FRAME in frame_vector.
-   If FRAME isn't in frame_vector yet, put it in,
-   lengthening the vector if necessary.  */
+#ifdef USE_X_TOOLKIT
+
+/* Return the frame whose ->display.x->id equals ID, or 0 if none.  */
 
-static int
-frame_vector_add_frame (f)
-     FRAME_PTR *f;
+static struct frame *
+menubar_id_to_frame (id)
+     LWLIB_ID id;
 {
-  int length = XVECTOR (frame_vector)->size;
-  int i, empty = -1;
-  Lisp_Object new, frame;
-
-  XSETFRAME (frame, f);
+  Lisp_Object tail, frame;
+  FRAME_PTR f;
 
-  for (i = 0; i < length; i++)
-    {
-      if (EQ (frame, XVECTOR (frame_vector)->contents[i]))
-	return i;
-      if (NILP (XVECTOR (frame_vector)->contents[i]))
-	empty = i;
-    }
-
-  if (empty >= 0)
+  for (tail = Vframe_list; GC_CONSP (tail); tail = XCONS (tail)->cdr)
     {
-      XVECTOR (frame_vector)->contents[empty] = frame;
-      return empty;
+      frame = XCONS (tail)->car;
+      if (!GC_FRAMEP (frame))
+        continue;
+      f = XFRAME (frame);
+      if (f->display.nothing == 1)
+	continue;
+      if (f->display.x->id == id)
+	return f;
     }
+  return 0;
+}
 
-  new = Fmake_vector (make_number (length * 2), Qnil);
-  bcopy (XVECTOR (frame_vector)->contents,
-	 XVECTOR (new)->contents, sizeof (Lisp_Object) * length);
-  
-  frame_vector = new;
-  XVECTOR (frame_vector)->contents[length] = frame;
-  return length;
-}
+#endif
 
 /* Initialize the menu_items structure if we haven't already done so.
    Also mark it as currently empty.  */
@@ -1139,6 +1134,36 @@
     }
 }
 
+/* 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.
+
+   To activate the menu bar, we use the X button-press event
+   that was saved in saved_button_event.
+   That makes the toolkit do its thing.
+
+   But first we recompute the menu bar contents (the whole tree).
+
+   The reason for saving the button event until here, instead of
+   passing it to the toolkit right away, is that we can safely
+   execute Lisp code.  */
+   
+x_activate_menubar (f)
+     FRAME_PTR f;
+{
+  if (f->display.x->saved_button_event->type != ButtonPress)
+    return;
+
+  set_frame_menubar (f, 0, 1);
+
+  BLOCK_INPUT;
+  XtDispatchEvent ((XEvent *) f->display.x->saved_button_event);
+  UNBLOCK_INPUT;
+
+  /* Ignore this if we get it a second time.  */
+  f->display.x->saved_button_event->type = 0;
+}
+
 /* Detect if a dialog or menu has been posted.  */
 
 int
@@ -1172,7 +1197,7 @@
      XtPointer client_data;
 {
   Lisp_Object prefix, entry;
-  FRAME_PTR f = XFRAME (XVECTOR (frame_vector)->contents[id]);
+  FRAME_PTR f = menubar_id_to_frame (id);
   Lisp_Object vector;
   Lisp_Object *subprefix_stack;
   int submenu_depth = 0;
@@ -1513,101 +1538,163 @@
    it is set the first time this is called, from initialize_frame_menubar.  */
 
 void
-set_frame_menubar (f, first_time)
+set_frame_menubar (f, first_time, deep_p)
      FRAME_PTR f;
      int first_time;
+     int deep_p;
 {
   Widget menubar_widget = f->display.x->menubar_widget;
   Lisp_Object tail, items, frame;
   widget_value *wv, *first_wv, *prev_wv = 0;
-  int previous_menu_items_used = f->menu_bar_items_used;
-  Lisp_Object *previous_items
-    = (Lisp_Object *) alloca (previous_menu_items_used * sizeof (Lisp_Object));
   int i;
-  int id;
-  int count;
-  int specpdl_count = specpdl_ptr - specpdl;
+  LWLIB_ID id;
 
-  count = inhibit_garbage_collection ();
+  if (f->display.x->id == 0)
+    f->display.x->id = next_menubar_widget_id++;
+  id = f->display.x->id;
 
-  specbind (Qinhibit_quit, Qt);
-  /* Don't let the debugger step into this code
-     because it is not reentrant.  */
-  specbind (Qdebug_on_next_call, Qnil);
-
-  id = frame_vector_add_frame (f);
+  if (! menubar_widget)
+    deep_p = 1;
 
   wv = malloc_widget_value ();
   wv->name = "menubar";
   wv->value = 0;
   wv->enabled = 1;
   first_wv = wv;
-  items = FRAME_MENU_BAR_ITEMS (f);
+
+  if (deep_p)
+    {
+      /* Make a widget-value tree representing the entire menu trees.  */
+
+      struct buffer *prev = current_buffer;
+      Lisp_Object buffer;
+      int specpdl_count = specpdl_ptr - specpdl;
+      int previous_menu_items_used = f->menu_bar_items_used;
+      Lisp_Object *previous_items
+	= (Lisp_Object *) alloca (previous_menu_items_used
+				  * sizeof (Lisp_Object));
 
-  /* Save the frame's previous menu bar contents data.  */
-  bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
-	 previous_menu_items_used * sizeof (Lisp_Object));
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
+      specbind (Qinhibit_quit, Qt);
+      /* Don't let the debugger step into this code
+	 because it is not reentrant.  */
+      specbind (Qdebug_on_next_call, Qnil);
+
+      record_unwind_protect (Fstore_match_data, Fmatch_data ());
+      if (NILP (Voverriding_local_map_menu_flag))
+	{
+	  specbind (Qoverriding_terminal_local_map, Qnil);
+	  specbind (Qoverriding_local_map, Qnil);
+	}
+
+      set_buffer_internal_1 (XBUFFER (buffer));
 
-  /* Fill in the current menu bar contents.  */
-  menu_items = f->menu_bar_vector;
-  menu_items_allocated = XVECTOR (menu_items)->size;
-  init_menu_items ();
-  for (i = 0; i < XVECTOR (items)->size; i += 3)
-    {
-      Lisp_Object key, string, maps;
+      /* Run the Lucid hook.  */
+      call1 (Vrun_hooks, Qactivate_menubar_hook);
+      /* If it has changed current-menubar from previous value,
+	 really recompute the menubar from the value.  */
+      if (! NILP (Vlucid_menu_bar_dirty_flag))
+	call0 (Qrecompute_lucid_menubar);
+      call1 (Vrun_hooks, Qmenu_bar_update_hook);
+      FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
+
+      items = FRAME_MENU_BAR_ITEMS (f);
+
+      inhibit_garbage_collection ();
+
+      /* Save the frame's previous menu bar contents data.  */
+      bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
+	     previous_menu_items_used * sizeof (Lisp_Object));
+
+      /* Fill in the current menu bar contents.  */
+      menu_items = f->menu_bar_vector;
+      menu_items_allocated = XVECTOR (menu_items)->size;
+      init_menu_items ();
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+	{
+	  Lisp_Object key, string, maps;
+
+	  key = XVECTOR (items)->contents[i];
+	  string = XVECTOR (items)->contents[i + 1];
+	  maps = XVECTOR (items)->contents[i + 2];
+	  if (NILP (string))
+	    break;
 
-      key = XVECTOR (items)->contents[i];
-      string = XVECTOR (items)->contents[i + 1];
-      maps = XVECTOR (items)->contents[i + 2];
-      if (NILP (string))
-	break;
+	  wv = single_submenu (key, string, maps);
+	  if (prev_wv) 
+	    prev_wv->next = wv;
+	  else
+	    first_wv->contents = wv;
+	  /* Don't set wv->name here; GC during the loop might relocate it.  */
+	  wv->enabled = 1;
+	  prev_wv = wv;
+	}
+
+      finish_menu_items ();
+
+      set_buffer_internal_1 (prev);
+      unbind_to (specpdl_count, Qnil);
+
+      /* If there has been no change in the Lisp-level contents
+	 of the menu bar, skip redisplaying it.  Just exit.  */
+
+      for (i = 0; i < previous_menu_items_used; i++)
+	if (menu_items_used == i
+	    || (previous_items[i] != XVECTOR (menu_items)->contents[i]))
+	  break;
+      if (i == menu_items_used && i == previous_menu_items_used)
+	{
+	  free_menubar_widget_value_tree (first_wv);
+	  menu_items = Qnil;
+
+	  return;
+	}
 
-      wv = single_submenu (key, string, maps);
-      if (prev_wv) 
-	prev_wv->next = wv;
-      else
-	first_wv->contents = wv;
-      /* Don't set wv->name here; GC during the loop might relocate it.  */
-      wv->enabled = 1;
-      prev_wv = wv;
+      /* Now GC cannot happen during the lifetime of the widget_value,
+	 so it's safe to store data from a Lisp_String.  */
+      wv = first_wv->contents;
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+	{
+	  Lisp_Object string;
+	  string = XVECTOR (items)->contents[i + 1];
+	  if (NILP (string))
+	    break;
+	  wv->name = (char *) XSTRING (string)->data;
+	  wv = wv->next;
+	}
+
+      f->menu_bar_vector = menu_items;
+      f->menu_bar_items_used = menu_items_used;
+      menu_items = Qnil;
+    }
+  else
+    {
+      /* Make a widget-value tree containing
+	 just the top level menu bar strings.  */
+
+      items = FRAME_MENU_BAR_ITEMS (f);
+      for (i = 0; i < XVECTOR (items)->size; i += 3)
+	{
+	  Lisp_Object string;
+
+	  string = XVECTOR (items)->contents[i + 1];
+	  if (NILP (string))
+	    break;
+
+	  wv = malloc_widget_value ();
+	  wv->name = (char *) XSTRING (string)->data;
+	  wv->value = 0;
+	  wv->enabled = 1;
+
+	  if (prev_wv) 
+	    prev_wv->next = wv;
+	  else
+	    first_wv->contents = wv;
+	  prev_wv = wv;
+	}
     }
 
-  finish_menu_items ();
-
-  /* If there has been no change in the Lisp-level contents
-     of the menu bar, skip redisplaying it.  Just exit.  */
-
-  for (i = 0; i < previous_menu_items_used; i++)
-    if (menu_items_used == i
-	|| (previous_items[i] != XVECTOR (menu_items)->contents[i]))
-      break;
-  if (i == menu_items_used && i == previous_menu_items_used)
-    {
-      free_menubar_widget_value_tree (first_wv);
-      menu_items = Qnil;
-
-      unbind_to (specpdl_count, Qnil);
-      return;
-    }
-
-  /* Now GC cannot happen during the lifetime of the widget_value,
-     so it's safe to store data from a Lisp_String.  */
-  wv = first_wv->contents;
-  for (i = 0; i < XVECTOR (items)->size; i += 3)
-    {
-      Lisp_Object string;
-      string = XVECTOR (items)->contents[i + 1];
-      if (NILP (string))
-	break;
-      wv->name = (char *) XSTRING (string)->data;
-      wv = wv->next;
-    }
-
-  f->menu_bar_vector = menu_items;
-  f->menu_bar_items_used = menu_items_used;
-  menu_items = Qnil;
-
-  unbind_to (count, Qnil);
+  /* Create or update the menu bar widget.  */
 
   BLOCK_INPUT;
 
@@ -1618,15 +1705,14 @@
 
       /* The third arg is DEEP_P, which says to consider the entire
 	 menu trees we supply, rather than just the menu bar item names.  */
-      lw_modify_all_widgets ((LWLIB_ID) id, first_wv, 1);
+      lw_modify_all_widgets (id, first_wv, deep_p);
 
       /* Re-enable the edit widget to resize.  */
       lw_allow_resizing (f->display.x->widget, True);
     }
   else
     {
-      menubar_widget = lw_create_widget ("menubar", "menubar", 
-					 (LWLIB_ID) id, first_wv, 
+      menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv, 
 					 f->display.x->column_widget,
 					 0,
 					 popup_activate_callback,
@@ -1658,8 +1744,6 @@
   update_frame_menubar (f);
 
   UNBLOCK_INPUT;
-
-  unbind_to (specpdl_count, Qnil);
 }
 
 /* Called from Fx_create_frame to create the inital menubar of a frame
@@ -1674,7 +1758,7 @@
   /* This function is called before the first chance to redisplay
      the frame.  It has to be, so the frame will have the right size.  */
   FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
-  set_frame_menubar (f, 1);
+  set_frame_menubar (f, 1, 1);
 }
 
 /* Get rid of the menu bar of frame F, and free its storage.
@@ -1691,10 +1775,8 @@
   
   if (menubar_widget)
     {
-      id = frame_vector_add_frame (f);
       BLOCK_INPUT;
-      lw_destroy_all_widgets ((LWLIB_ID) id);
-      XVECTOR (frame_vector)->contents[id] = Qnil;
+      lw_destroy_all_widgets ((LWLIB_ID) f->display.x->id);
       UNBLOCK_INPUT;
     }
 }
@@ -1723,10 +1805,10 @@
    library.
 
    For the main windows, and popup menus, we use this counter,
-   which we increment each time after use.
+   which we increment each time after use.  This starts from 1<<16.
 
-   For menu bars, we use the index of the frame in frame_vector
-   as the id.  */
+   For menu bars, we use numbers starting at 0, counted in
+   next_menubar_widget_id.  */
 LWLIB_ID widget_id_tick;
 
 #ifdef __STDC__
@@ -2478,11 +2560,9 @@
 
 #ifdef USE_X_TOOLKIT
   widget_id_tick = (1<<16);	
+  next_menubar_widget_id = 1;
 #endif
 
-  staticpro (&frame_vector);
-  frame_vector = Fmake_vector (make_number (10), Qnil);
-
   defsubr (&Sx_popup_menu);
   defsubr (&Sx_popup_dialog);
 }