use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / w32menu.c
index b52aae5..19eac13 100644 (file)
@@ -1,5 +1,5 @@
 /* Menu support for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 1986, 1988, 1993-1994, 1996, 1998-1999, 2001-2013 Free
+   Copyright (C) 1986, 1988, 1993-1994, 1996, 1998-1999, 2001-2014 Free
    Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -82,8 +82,8 @@ typedef BOOL (WINAPI * SetMenuItemInfoA_Proc) (
     IN LPCMENUITEMINFOA);
 typedef int (WINAPI * MessageBoxW_Proc) (
     IN HWND window,
-    IN WCHAR *text,
-    IN WCHAR *caption,
+    IN const WCHAR *text,
+    IN const WCHAR *caption,
     IN UINT type);
 
 #ifdef NTGUI_UNICODE
@@ -98,12 +98,12 @@ AppendMenuW_Proc unicode_append_menu = NULL;
 MessageBoxW_Proc unicode_message_box = NULL;
 #endif /* NTGUI_UNICODE */
 
-Lisp_Object Qdebug_on_next_call;
+Lisp_Object Qdebug_on_next_call, Qunsupported__w32_dialog;
 
 void set_frame_menubar (struct frame *, bool, bool);
 
 #ifdef HAVE_DIALOGS
-static Lisp_Object w32_dialog_show (struct frame *, int, Lisp_Object, char**);
+static Lisp_Object w32_dialog_show (struct frame *, Lisp_Object, Lisp_Object, char **);
 #else
 static int is_simple_dialog (Lisp_Object);
 static Lisp_Object simple_dialog_show (struct frame *, Lisp_Object, Lisp_Object);
@@ -114,128 +114,42 @@ static int fill_in_menu (HMENU, widget_value *);
 
 void w32_free_menu_strings (HWND);
 
-#ifdef HAVE_MENUS
-
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
-       doc: /* Pop up a dialog box and return user's selection.
-POSITION specifies which frame to use.
-This is normally a mouse button event or a window or frame.
-If POSITION is t, it means to use the frame the mouse is on.
-The dialog box appears in the middle of the specified frame.
-
-CONTENTS specifies the alternatives to display in the dialog box.
-It is a list of the form (TITLE ITEM1 ITEM2...).
-Each ITEM is a cons cell (STRING . VALUE).
-The return value is VALUE from the chosen item.
-
-An ITEM may also be just a string--that makes a nonselectable item.
-An ITEM may also be nil--that means to put all preceding items
-on the left of the dialog box and all following items on the right.
-\(By default, approximately half appear on each side.)
-
-If HEADER is non-nil, the frame title for the box is "Information",
-otherwise it is "Question". */)
-  (Lisp_Object position, Lisp_Object contents, Lisp_Object header)
+Lisp_Object
+w32_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
 {
-  struct frame *f = NULL;
-  Lisp_Object window;
-
-  /* Decode the first argument: find the window or frame to use.  */
-  if (EQ (position, Qt)
-      || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
-                               || EQ (XCAR (position), Qtool_bar))))
-    {
-#if 0 /* Using the frame the mouse is on may not be right.  */
-      /* Use the mouse's current position.  */
-      struct frame *new_f = SELECTED_FRAME ();
-      Lisp_Object bar_window;
-      enum scroll_bar_part part;
-      Time time;
-      Lisp_Object x, y;
-
-      (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
-
-      if (new_f != 0)
-       XSETFRAME (window, new_f);
-      else
-       window = selected_window;
-#endif
-      window = selected_window;
-    }
-  else if (CONSP (position))
-    {
-      Lisp_Object tem = XCAR (position);
-      if (CONSP (tem))
-       window = Fcar (XCDR (position));
-      else
-       {
-         tem = Fcar (XCDR (position));  /* EVENT_START (position) */
-         window = Fcar (tem);       /* POSN_WINDOW (tem) */
-       }
-    }
-  else if (WINDOWP (position) || FRAMEP (position))
-    window = position;
-  else
-    window = Qnil;
-
-  /* Decode where to put the menu.  */
-
-  if (FRAMEP (window))
-    f = XFRAME (window);
-  else if (WINDOWP (window))
-    {
-      CHECK_LIVE_WINDOW (window);
-      f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
-    }
-  else
-    /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
-       but I don't want to make one now.  */
-    CHECK_WINDOW (window);
 
   check_window_system (f);
 
 #ifndef HAVE_DIALOGS
 
-  {
-    /* Handle simple Yes/No choices as MessageBox popups.  */
-    if (is_simple_dialog (contents))
-      return simple_dialog_show (f, contents, header);
-    else
-      {
-       /* Display a menu with these alternatives
-          in the middle of frame F.  */
-       Lisp_Object x, y, frame, newpos;
-       XSETFRAME (frame, f);
-       XSETINT (x, FRAME_PIXEL_WIDTH (f) / 2);
-       XSETINT (y, FRAME_PIXEL_HEIGHT (f) / 2);
-       newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
-       return Fx_popup_menu (newpos,
-                             Fcons (Fcar (contents), Fcons (contents, Qnil)));
-      }
-  }
-#else /* HAVE_DIALOGS */
-  {
-    Lisp_Object title;
-    char *error_name;
-    Lisp_Object selection;
+  /* Handle simple Yes/No choices as MessageBox popups.  */
+  if (is_simple_dialog (contents))
+    return simple_dialog_show (f, contents, header);
+  else
+    return Qunsupported__w32_dialog;
+#else  /* HAVE_DIALOGS */
+    {
+      Lisp_Object title;
+      char *error_name;
+      Lisp_Object selection;
 
-    /* Decode the dialog items from what was specified.  */
-    title = Fcar (contents);
-    CHECK_STRING (title);
+      /* Decode the dialog items from what was specified.  */
+      title = Fcar (contents);
+      CHECK_STRING (title);
 
-    list_of_panes (Fcons (contents, Qnil));
+      list_of_panes (Fcons (contents, Qnil));
 
-    /* Display them in a dialog box.  */
-    block_input ();
-    selection = w32_dialog_show (f, 0, title, header, &error_name);
-    unblock_input ();
+      /* Display them in a dialog box.  */
+      block_input ();
+      selection = w32_dialog_show (f, title, header, &error_name);
+      unblock_input ();
 
-    discard_menu_items ();
-    FRAME_DISPLAY_INFO (f)->grabbed = 0;
+      discard_menu_items ();
+      FRAME_DISPLAY_INFO (f)->grabbed = 0;
 
-    if (error_name) error (error_name);
-    return selection;
-  }
+      if (error_name) error (error_name);
+      return selection;
+    }
 #endif /* HAVE_DIALOGS */
 }
 
@@ -385,7 +299,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
 
       struct buffer *prev = current_buffer;
       Lisp_Object buffer;
-      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+      dynwind_begin ();
       int previous_menu_items_used = f->menu_bar_items_used;
       Lisp_Object *previous_items
        = (Lisp_Object *) alloca (previous_menu_items_used
@@ -421,7 +335,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
 
       /* Save the frame's previous menu bar contents data.  */
       if (previous_menu_items_used)
-       memcpy (previous_items, XVECTOR (f->menu_bar_vector)->u.contents,
+       memcpy (previous_items, XVECTOR (f->menu_bar_vector)->contents,
                previous_menu_items_used * word_size);
 
       /* Fill in menu_items with the current menu bar contents.
@@ -462,12 +376,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       /* Convert menu_items into widget_value trees
         to display the menu.  This cannot evaluate Lisp code.  */
 
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       for (i = 0; i < last_i; i += 4)
@@ -498,7 +408,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
        {
          free_menubar_widget_value_tree (first_wv);
          discard_menu_items ();
-          unbind_to (specpdl_count, Qnil);
+          dynwind_end ();
          return;
        }
 
@@ -506,7 +416,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       f->menu_bar_items_used = menu_items_used;
 
       /* This undoes save_menu_items.  */
-      unbind_to (specpdl_count, Qnil);
+      dynwind_end ();
 
       /* Now GC cannot happen during the lifetime of the widget_value,
         so it's safe to store data from a Lisp_String, as long as
@@ -530,12 +440,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
       /* Make a widget-value tree containing
         just the top level menu bar strings.  */
 
-      wv = xmalloc_widget_value ();
-      wv->name = "menubar";
-      wv->value = 0;
-      wv->enabled = 1;
+      wv = make_widget_value ("menubar", NULL, true, Qnil);
       wv->button_type = BUTTON_TYPE_NONE;
-      wv->help = Qnil;
       first_wv = wv;
 
       items = FRAME_MENU_BAR_ITEMS (f);
@@ -547,12 +453,8 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
          if (NILP (string))
            break;
 
-         wv = xmalloc_widget_value ();
-         wv->name = SSDATA (string);
-         wv->value = 0;
-         wv->enabled = 1;
+         wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
          wv->button_type = BUTTON_TYPE_NONE;
-         wv->help = Qnil;
          /* This prevents lwlib from assuming this
             menu item is really supposed to be empty.  */
          /* The EMACS_INT cast avoids a warning.
@@ -601,7 +503,7 @@ set_frame_menubar (struct frame *f, bool first_time, bool deep_p)
     /* Force the window size to be recomputed so that the frame's text
        area remains the same, if menubar has just been created.  */
     if (old_widget == NULL)
-      x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f));
+      x_set_window_size (f, 0, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f), 1);
   }
 
   unblock_input ();
@@ -647,8 +549,9 @@ free_frame_menubar (struct frame *f)
 /* F is the frame the menu is for.
    X and Y are the frame-relative specified position,
    relative to the inside upper left corner of the frame F.
-   FOR_CLICK is nonzero if this menu was invoked for a mouse click.
-   KEYMAPS is 1 if this menu was specified with keymaps;
+   Bitfield MENUFLAGS bits are:
+   MENU_FOR_CLICK is set if this menu was invoked for a mouse click.
+   MENU_KEYMAPS is set if this menu was specified with keymaps;
     in that case, we return a list containing the chosen item's value
     and perhaps also the pane's prefix.
    TITLE is the specified menu title.
@@ -656,7 +559,7 @@ free_frame_menubar (struct frame *f)
    (We return nil on failure, but the value doesn't actually matter.)  */
 
 Lisp_Object
-w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
+w32_menu_show (struct frame *f, int x, int y, int menuflags,
               Lisp_Object title, const char **error)
 {
   int i;
@@ -682,14 +585,12 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
       return Qnil;
     }
 
+  block_input ();
+
   /* Create a tree of widget_value objects
      representing the panes and their items.  */
-  wv = xmalloc_widget_value ();
-  wv->name = "menu";
-  wv->value = 0;
-  wv->enabled = 1;
+  wv = make_widget_value ("menu", NULL, true, Qnil);
   wv->button_type = BUTTON_TYPE_NONE;
-  wv->help = Qnil;
   first_wv = wv;
   first_pane = 1;
 
@@ -723,7 +624,7 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
        {
          /* Create a new pane.  */
          Lisp_Object pane_name, prefix;
-         char *pane_string;
+         const char *pane_string;
          pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
 
@@ -747,20 +648,16 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
          /* If the pane has a meaningful name,
             make the pane a top-level menu item
             with its items as a submenu beneath it.  */
-         if (!keymaps && strcmp (pane_string, ""))
+         if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, ""))
            {
-             wv = xmalloc_widget_value ();
+             wv = make_widget_value (pane_string, NULL, true, Qnil);
              if (save_wv)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
-             wv->name = pane_string;
-             if (keymaps && !NILP (prefix))
+             if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
                wv->name++;
-             wv->value = 0;
-             wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
-             wv->help = Qnil;
              save_wv = wv;
              prev_wv = 0;
            }
@@ -801,19 +698,17 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
              ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
            }
 
-         wv = xmalloc_widget_value ();
+         wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
+                                 STRINGP (help) ? help : Qnil);
          if (prev_wv)
            prev_wv->next = wv;
          else
            save_wv->contents = wv;
-         wv->name = SSDATA (item_name);
          if (!NILP (descrip))
            wv->key = SSDATA (descrip);
-         wv->value = 0;
          /* Use the contents index as call_data, since we are
              restricted to 16-bits.  */
          wv->call_data = !NILP (def) ? (void *) (EMACS_INT) i : 0;
-         wv->enabled = !NILP (enable);
 
          if (NILP (type))
            wv->button_type = BUTTON_TYPE_NONE;
@@ -826,11 +721,6 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
 
          wv->selected = !NILP (selected);
 
-          if (!STRINGP (help))
-           help = Qnil;
-
-         wv->help = help;
-
          prev_wv = wv;
 
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -840,25 +730,21 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
   /* Deal with the title, if it is non-nil.  */
   if (!NILP (title))
     {
-      widget_value *wv_title = xmalloc_widget_value ();
-      widget_value *wv_sep = xmalloc_widget_value ();
+      widget_value *wv_title;
+      widget_value *wv_sep = make_widget_value ("--", NULL, false, Qnil);
 
       /* Maybe replace this separator with a bitmap or owner-draw item
         so that it looks better.  Having two separators looks odd.  */
-      wv_sep->name = "--";
       wv_sep->next = first_wv->contents;
-      wv_sep->help = Qnil;
 
       if (unicode_append_menu)
        title = ENCODE_UTF_8 (title);
       else if (STRING_MULTIBYTE (title))
        title = ENCODE_SYSTEM (title);
 
-      wv_title->name = SSDATA (title);
-      wv_title->enabled = TRUE;
+      wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil);
       wv_title->title = TRUE;
       wv_title->button_type = BUTTON_TYPE_NONE;
-      wv_title->help = Qnil;
       wv_title->next = wv_sep;
       first_wv->contents = wv_title;
     }
@@ -926,10 +812,10 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
            i += 1;
          else
            {
-             entry     = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+             entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
              if (menu_item_selection == i)
                {
-                 if (keymaps != 0)
+                 if (menuflags & MENU_KEYMAPS)
                    {
                      int j;
 
@@ -940,16 +826,21 @@ w32_menu_show (struct frame *f, int x, int y, int for_click, int keymaps,
                        if (!NILP (subprefix_stack[j]))
                          entry = Fcons (subprefix_stack[j], entry);
                    }
+                 unblock_input ();
                  return entry;
                }
              i += MENU_ITEMS_ITEM_LENGTH;
            }
        }
     }
-  else if (!for_click)
-    /* Make "Cancel" equivalent to C-g.  */
-    Fsignal (Qquit, Qnil);
+  else if (!(menuflags & MENU_FOR_CLICK))
+    {
+      unblock_input ();
+      /* Make "Cancel" equivalent to C-g.  */
+      Fsignal (Qquit, Qnil);
+    }
 
+  unblock_input ();
   return Qnil;
 }
 \f
@@ -983,9 +874,8 @@ static char * button_names [] = {
   "button6", "button7", "button8", "button9", "button10" };
 
 static Lisp_Object
-w32_dialog_show (struct frame *f, int keymaps,
-                Lisp_Object title, Lisp_Object header,
-                char **error)
+w32_dialog_show (struct frame *f, Lisp_Object title,
+                Lisp_Object header, char **error)
 {
   int i, nb_buttons = 0;
   char dialog_name[6];
@@ -1009,19 +899,12 @@ w32_dialog_show (struct frame *f, int keymaps,
   /* Create a tree of widget_value objects
      representing the text label and buttons.  */
   {
-    Lisp_Object pane_name, prefix;
+    Lisp_Object pane_name;
     char *pane_string;
     pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
-    prefix = AREF (menu_items, MENU_ITEMS_PANE_PREFIX);
     pane_string = (NILP (pane_name)
                   ? "" : SSDATA (pane_name));
-    prev_wv = xmalloc_widget_value ();
-    prev_wv->value = pane_string;
-    if (keymaps && !NILP (prefix))
-      prev_wv->name++;
-    prev_wv->enabled = 1;
-    prev_wv->name = "message";
-    prev_wv->help = Qnil;
+    prev_wv = make_widget_value ("message", pane_string, true, Qnil);
     first_wv = prev_wv;
 
     /* Loop over all panes and items, filling in the tree.  */
@@ -1058,15 +941,13 @@ w32_dialog_show (struct frame *f, int keymaps,
            return Qnil;
          }
 
-       wv = xmalloc_widget_value ();
+       wv = make_widget_value (button_names[nb_buttons],
+                               SSDATA (item_name),
+                               !NILP (enable), Qnil);
        prev_wv->next = wv;
-       wv->name = (char *) button_names[nb_buttons];
        if (!NILP (descrip))
          wv->key = SSDATA (descrip);
-       wv->value = SSDATA (item_name);
        wv->call_data = aref_addr (menu_items, i);
-       wv->enabled = !NILP (enable);
-       wv->help = Qnil;
        prev_wv = wv;
 
        if (! boundary_seen)
@@ -1081,9 +962,7 @@ w32_dialog_show (struct frame *f, int keymaps,
     if (! boundary_seen)
       left_count = nb_buttons - nb_buttons / 2;
 
-    wv = xmalloc_widget_value ();
-    wv->name = dialog_name;
-    wv->help = Qnil;
+    wv = make_widget_value (dialog_name, NULL, false, Qnil);
 
     /*  Frame title: 'Q' = Question, 'I' = Information.
         Can also have 'E' = Error if, one day, we want
@@ -1131,32 +1010,18 @@ w32_dialog_show (struct frame *f, int keymaps,
      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 (AREF (menu_items, i), Qt))
-           {
-             prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
-             i += MENU_ITEMS_PANE_LENGTH;
-           }
+           i += MENU_ITEMS_PANE_LENGTH;
          else
            {
-             entry     = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+             entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
              if (menu_item_selection == i)
-               {
-                 if (keymaps != 0)
-                   {
-                     entry = Fcons (entry, Qnil);
-                     if (!NILP (prefix))
-                       entry = Fcons (prefix, entry);
-                   }
-                 return entry;
-               }
+               return entry;
              i += MENU_ITEMS_ITEM_LENGTH;
            }
        }
@@ -1234,7 +1099,8 @@ simple_dialog_show (struct frame *f, Lisp_Object contents, Lisp_Object header)
   /* Use Unicode if possible, so any language can be displayed.  */
   if (unicode_message_box)
     {
-      WCHAR *text, *title;
+      WCHAR *text;
+      const WCHAR *title;
       USE_SAFE_ALLOCA;
 
       if (STRINGP (temp))
@@ -1269,7 +1135,7 @@ simple_dialog_show (struct frame *f, Lisp_Object contents, Lisp_Object header)
     }
   else
     {
-      char *text, *title;
+      const char *text, *title;
 
       /* Fall back on ANSI message box, but at least use system
         encoding so questions representable by the system codepage
@@ -1690,36 +1556,28 @@ w32_free_menu_strings (HWND hwnd)
   current_popup_menu = NULL;
 }
 
-#endif /* HAVE_MENUS */
-
 /* The following is used by delayed window autoselection.  */
 
 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
        doc: /* Return t if a menu or popup dialog is active on selected frame.  */)
   (void)
 {
-#ifdef HAVE_MENUS
   struct frame *f;
   f = SELECTED_FRAME ();
   return (f->output_data.w32->menubar_active > 0) ? Qt : Qnil;
-#else
-  return Qnil;
-#endif /* HAVE_MENUS */
 }
 
 void
 syms_of_w32menu (void)
 {
+#include "w32menu.x"
+
   globals_of_w32menu ();
 
   current_popup_menu = NULL;
 
   DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
-
-  defsubr (&Smenu_or_popup_active_p);
-#ifdef HAVE_MENUS
-  defsubr (&Sx_popup_dialog);
-#endif
+  DEFSYM (Qunsupported__w32_dialog, "unsupported--w32-dialog");
 }
 
 /*