Unspecified location of chown for Linux (Linux doesn't put chown in /etc).
[bpt/emacs.git] / src / xmenu.c
index 354bf77..87a74a1 100644 (file)
@@ -119,6 +119,9 @@ static void list_of_items ();
    A single vector slot containing lambda indicates the end of a submenu.
    The submenu follows a menu item which is the way to reach the submenu.
 
+   A single vector slot containing quote indicates that the
+   following items should appear on the right of a dialog box.
+
    Using a Lisp vector to hold this information while we decode it
    takes care of protecting all the data from GC.  */
 
@@ -226,6 +229,17 @@ push_submenu_end ()
   menu_items_submenu_depth--;
 }
 
+/* Indicate boundary between left and right.  */
+
+static void
+push_left_right_boundary ()
+{
+  if (menu_items_used + 1 > menu_items_allocated)
+    grow_menu_items ();
+
+  XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
+}
+
 /* Start a new menu pane in menu_items..
    NAME is the pane name.  PREFIX_VEC is a prefix key for this pane.  */
 
@@ -609,6 +623,8 @@ list_of_items (pane)
       item = Fcar (tail);
       if (STRINGP (item))
        push_menu_item (item, Qnil, Qnil, Qnil);
+      else if (NILP (item))
+       push_left_right_boundary ();
       else
        {
          CHECK_CONS (item, 0);
@@ -623,7 +639,7 @@ DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
   "Pop up a deck-of-cards menu 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\
+where XOFFSET and YOFFSET are positions in pixels 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\
@@ -730,7 +746,7 @@ cached information about equivalent key sequences.")
          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);
+         ypos = (f->display.x->line_height * XWINDOW (window)->top);
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -844,7 +860,11 @@ The dialog box appears in the middle of the specified frame.\n\
 CONTENTS specifies the alternatives to display in the dialog box.\n\
 It is a list of the form (TITLE ITEM1 ITEM2...).\n\
 Each ITEM is a cons cell (STRING . VALUE).\n\
-The return value is VALUE from the chosen item.")
+The return value is VALUE from the chosen item.\n\n\
+An ITEM may also be just a string--that makes a nonselectable item.\n\
+An ITEM may also be nil--that means to put all preceding items\n\
+on the left of the dialog box and all following items on the right.\n\
+\(By default, approximately half appear on each side.)")
   (position, contents)
      Lisp_Object position, contents;
 {
@@ -856,6 +876,7 @@ The return value is VALUE from the chosen item.")
   /* Decode the first argument: find the window or frame to use.  */
   if (EQ (position, Qt))
     {
+#if 0 /* Using the frame the mouse is on may not be right.  */
       /* Use the mouse's current position.  */
       FRAME_PTR new_f = 0;
       Lisp_Object bar_window;
@@ -869,6 +890,10 @@ The return value is VALUE from the chosen item.")
        XSET (window, Lisp_Frame, new_f);
       else
        window = selected_window;
+#endif
+      /* Decode the first argument: find the window and the coordinates.  */
+      if (EQ (position, Qt))
+       window = selected_window;
     }
   else if (CONSP (position))
     {
@@ -956,7 +981,7 @@ dispatch_dummy_expose (w, x, y)
   dummy.x = x;
   dummy.y = y;
 
-  XtDispatchEvent (&dummy);
+  XtDispatchEvent ((XEvent *) &dummy);
 }
 
 static int
@@ -1093,7 +1118,7 @@ free_menubar_widget_value_tree (wv)
 extern void EmacsFrameSetCharSize ();
 
 static void
-update_one_frame_psheets (f)
+update_frame_menubar (f)
      FRAME_PTR f;
 {
   struct x_display *x = f->display.x;
@@ -1145,8 +1170,9 @@ update_one_frame_psheets (f)
 }
 
 void
-set_frame_menubar (f)
+set_frame_menubar (f, first_time)
      FRAME_PTR f;
+     int first_time;
 {
   Widget menubar_widget = f->display.x->menubar_widget;
   int id = (int) f;
@@ -1162,7 +1188,8 @@ set_frame_menubar (f)
   wv->enabled = 1;
   save_wv = first_wv = wv;
 
-  items = FRAME_MENU_BAR_ITEMS (f);
+  if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
+    items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
 
   for (i = 0; i < XVECTOR (items)->size; i += 3)
     {
@@ -1177,7 +1204,7 @@ set_frame_menubar (f)
        prev_wv->next = wv;
       else 
        save_wv->contents = wv;
-      wv->name = XSTRING (string)->data;
+      wv->name = (char *) XSTRING (string)->data;
       wv->value = 0;
       wv->enabled = 1;
       prev_wv = wv;
@@ -1202,7 +1229,9 @@ set_frame_menubar (f)
   
   free_menubar_widget_value_tree (first_wv);
 
-  update_one_frame_psheets (f);
+  /* Don't update the menubar the first time it is created via x_window.  */
+  if (!first_time)
+    update_frame_menubar (f);
 
   UNBLOCK_INPUT;
 }
@@ -1224,13 +1253,29 @@ free_frame_menubar (f)
       UNBLOCK_INPUT;
     }
 }
+/* Called from Fx_create_frame to create the inital menubar of a frame
+   before it is mapped, so that the window is mapped with the menubar already
+   there instead of us tacking it on later and thrashing the window after it
+   is visible.  */
+void
+initialize_frame_menubar (f)
+     FRAME_PTR f;
+{
+  set_frame_menubar (f, 1);
+}
 \f
-/* Nonzero if position X, Y relative to inside of frame F
-   is in some other menu bar item.  */
+/* Horizontal bounds of the current menu bar item.  */
 
 static int this_menu_bar_item_beg;
 static int this_menu_bar_item_end;
 
+/* Horizontal position of the end of the last menu bar item.  */
+
+static int last_menu_bar_item_end;
+
+/* Nonzero if position X, Y is in the menu bar and in some menu bar item
+   but not in the current menu bar item.  */
+
 static int
 other_menu_bar_item_p (f, x, y)
      FRAME_PTR f;
@@ -1239,7 +1284,7 @@ other_menu_bar_item_p (f, x, y)
   return (y >= 0
          && y < f->display.x->menubar_widget->core.height
          && x >= 0
-         && x < f->display.x->menubar_widget->core.width
+         && x < last_menu_bar_item_end
          && (x >= this_menu_bar_item_end
              || x < this_menu_bar_item_beg));
 }
@@ -1348,10 +1393,13 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   struct event_queue *queue = NULL;
   struct event_queue *queue_tmp;
 
+  Position root_x, root_y;
+
   *error = NULL;
 
   this_menu_bar_item_beg = -1;
   this_menu_bar_item_end = -1;
+  last_menu_bar_item_end = -1;
 
   /* Figure out which menu bar item, if any, this menu is for.  */
   if (menubarp)
@@ -1367,7 +1415,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          xend += (string_width (menubar, menubar_item->name) 
                   + 2 * (menubar->menu.horizontal_spacing
                          + menubar->menu.shadow_thickness));
-         if (x < xend)
+         if (x >= xbeg && x < xend)
            {
              x = xbeg + 4;
              y = 0;
@@ -1375,19 +1423,19 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                 to a different item.  */
              this_menu_bar_item_beg = xbeg;
              this_menu_bar_item_end = xend;
-             break;
            }
        }
+      last_menu_bar_item_end = xend;
     }
   if (menubar_item == 0)
     menubarp = 0;
 
   /* Offset the coordinates to root-relative.  */
-  x += (f->display.x->widget->core.x
-       + f->display.x->widget->core.border_width);
-  y += (f->display.x->widget->core.y 
-       + f->display.x->widget->core.border_width
-                + f->display.x->menubar_widget->core.height);
+  XtTranslateCoords (f->display.x->widget,
+                    x, y + f->display.x->menubar_widget->core.height,
+                    &root_x, &root_y);
+  x = root_x;
+  y = root_y;
 
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
@@ -1417,6 +1465,10 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
               && submenu_depth != 0)
        i += MENU_ITEMS_PANE_LENGTH;
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+       i += 1;
       else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
        {
          /* Create a new pane.  */
@@ -1465,9 +1517,9 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
            prev_wv->next = wv;
          else 
            save_wv->contents = wv;
-         wv->name = XSTRING (item_name)->data;
+         wv->name = (char *) XSTRING (item_name)->data;
          if (!NILP (descrip))
-           wv->key = XSTRING (descrip)->data;
+           wv->key = (char *) XSTRING (descrip)->data;
          wv->value = 0;
          wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
          wv->enabled = !NILP (enable);
@@ -1617,10 +1669,11 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
     }
 
-#if 0 /* No need to do that. The menu has disappeared.  */
+  /* fp turned off the following statement and wrote a comment
+     that it is unnecessary--that the menu has already disappeared.
+     I observer that is not so. -- rms.  */
   /* 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) 
@@ -1629,6 +1682,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       XPutBackEvent (XDISPLAY &queue_tmp->event);
       queue = queue_tmp->next;
       free ((char *)queue_tmp);
+      /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
+      interrupt_input_pending = 1;
     }
 
   /* Find the selected item, and its pane, to return
@@ -1674,7 +1729,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
                      if (!NILP (prefix))
                        entry = Fcons (prefix, entry);
                      for (j = submenu_depth - 1; j >= 0; j--)
-                       entry = Fcons (subprefix_stack[j], entry);
+                       if (!NILP (subprefix_stack[j]))
+                         entry = Fcons (subprefix_stack[j], entry);
                    }
                  return entry;
                }
@@ -1720,6 +1776,11 @@ xdialog_show (f, menubarp, keymaps, title, error)
   struct event_queue *queue = NULL;
   struct event_queue *queue_tmp;
 
+  /* Number of elements seen so far, before boundary.  */
+  int left_count = 0;
+  /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
+  int boundary_seen = 0;
+
   *error = NULL;
 
   if (menu_items_n_panes > 1)
@@ -1763,6 +1824,14 @@ xdialog_show (f, menubarp, keymaps, title, error)
            *error = "Submenu in dialog items";
            return Qnil;
          }
+       if (EQ (item_name, Qquote))
+         {
+           /* This is the boundary between left-side elts
+              and right-side elts.  Stop incrementing right_count.  */
+           boundary_seen = 1;
+           i++;
+           continue;
+         }
        if (nb_buttons >= 10)
          {
            free_menubar_widget_value_tree (first_wv);
@@ -1774,16 +1843,24 @@ xdialog_show (f, menubarp, keymaps, title, error)
        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->key = (char *) XSTRING (descrip)->data;
+       wv->value = (char *) XSTRING (item_name)->data;
        wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
        wv->enabled = !NILP (enable);
        prev_wv = wv;
 
+       if (! boundary_seen)
+         left_count++;
+
        nb_buttons++;
        i += MENU_ITEMS_ITEM_LENGTH;
       }
 
+    /* If the boundary was not specified,
+       by default put half on the left and half on the right.  */
+    if (! boundary_seen)
+      left_count = nb_buttons - nb_buttons / 2;
+
     wv = malloc_widget_value ();
     wv->name = dialog_name;
 
@@ -1795,11 +1872,11 @@ xdialog_show (f, menubarp, keymaps, title, error)
     dialog_name[1] = '0' + nb_buttons;
     dialog_name[2] = 'B';
     dialog_name[3] = 'R';
-    dialog_name[4] = '0' + nb_buttons / 2;
+    /* Number of buttons to put on the right.  */
+    dialog_name[4] = '0' + nb_buttons - left_count;
     dialog_name[5] = 0;
     wv->contents = first_wv;
     first_wv = wv;
-
   }
 
   /* Actually create the dialog.  */
@@ -1848,6 +1925,12 @@ xdialog_show (f, menubarp, keymaps, title, error)
     }
  pop_down:
 
+  /* State that no mouse buttons are now held.
+     That is not necessarily true, but the fiction leads to reasonable
+     results, and it is a pain to ask which are actually held now
+     or track this in the loop above.  */
+  x_mouse_grabbed = 0;
+
   /* Unread any events that we got but did not handle.  */
   while (queue != NULL) 
     {
@@ -1855,6 +1938,8 @@ xdialog_show (f, menubarp, keymaps, title, error)
       XPutBackEvent (XDISPLAY &queue_tmp->event);
       queue = queue_tmp->next;
       free ((char *)queue_tmp);
+      /* Cause these events to get read as soon as we UNBLOCK_INPUT.  */
+      interrupt_input_pending = 1;
     }
 
   /* Find the selected item, and its pane, to return
@@ -1914,7 +1999,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
   char *datap;
   int ulx, uly, width, height;
   int dispwidth, dispheight;
-  int i;
+  int i, j;
+  int maxwidth;
   int dummy_int;
   unsigned int dummy_uint;
 
@@ -1993,21 +2079,70 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
              return Qnil;
            }
          i += MENU_ITEMS_PANE_LENGTH;
+
+         /* Find the width of the widest item in this pane.  */
+         maxwidth = 0;
+         j = i;
+         while (j < menu_items_used)
+           {
+             Lisp_Object item;
+             item = XVECTOR (menu_items)->contents[j];
+             if (EQ (item, Qt))
+               break;
+             if (NILP (item))
+               {
+                 j++;
+                 continue;
+               }
+             width = XSTRING (item)->size;
+             if (width > maxwidth)
+               maxwidth = width;
+
+             j += MENU_ITEMS_ITEM_LENGTH;
+           }
        }
+      /* Ignore a nil in the item list.
+        It's meaningful only for dialog boxes.  */
+      else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
+       i += 1;
       else
        {
          /* Create a new item within current pane.  */
          Lisp_Object item_name, enable, descrip;
+         unsigned char *item_data;
 
          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];
          if (!NILP (descrip))
-           item_name = concat2 (item_name, descrip);
+           {
+             int gap = maxwidth - XSTRING (item_name)->size;
+#ifdef C_ALLOCA
+             Lisp_Object spacer;
+             spacer = Fmake_string (make_number (gap), make_number (' '));
+             item_name = concat2 (item_name, spacer);
+             item_name = concat2 (item_name, descrip);
+             item_data = XSTRING (item_name)->data;
+#else
+             /* if alloca is fast, use that to make the space,
+                to reduce gc needs.  */
+             item_data
+               = (unsigned char *) alloca (maxwidth
+                                           + XSTRING (descrip)->size + 1);
+             bcopy (XSTRING (item_name)->data, item_data,
+                    XSTRING (item_name)->size);
+             for (j = XSTRING (item_name)->size; j < maxwidth; j++)
+               item_data[j] = ' ';
+             bcopy (XSTRING (descrip)->data, item_data + j,
+                    XSTRING (descrip)->size);
+             item_data[j + XSTRING (descrip)->size] = 0;
+#endif
+           }
+         else
+           item_data = XSTRING (item_name)->data;
 
-         if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
-                                XSTRING (item_name)->data,
+         if (XMenuAddSelection (XDISPLAY menu, lpane, 0, item_data,
                                 !NILP (enable))
              == XM_FAILURE)
            {
@@ -2018,7 +2153,7 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
          i += MENU_ITEMS_ITEM_LENGTH;
        }
     }
-  
+
   /* All set and ready to fly.  */
   XMenuRecompute (XDISPLAY menu);
   dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
@@ -2041,7 +2176,8 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
     }
   if (ulx < 0) x -= ulx;
   if (uly < 0) y -= uly;
-    
+
+  XMenuSetAEQ (menu, TRUE);
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
   
@@ -2098,6 +2234,13 @@ xmenu_show (f, x, y, menubarp, keymaps, title, error)
       break;
     }
   XMenuDestroy (XDISPLAY menu);
+
+  /* State that no mouse buttons are now held.
+     (The oldXMenu code doesn't track this info for us.)
+     That is not necessarily true, but the fiction leads to reasonable
+     results, and it is a pain to ask which are actually held now.  */
+  x_mouse_grabbed = 0;
+
   return entry;
 }
 #endif /* not USE_X_TOOLKIT */