]> code.delx.au - gnu-emacs/blobdiff - src/w32menu.c
Use fast_string_match_ignore_case for comparing font names.
[gnu-emacs] / src / w32menu.c
index 632685c337d05d8896e8685e1a33151591a13efe..4553895d829069312a044229e5bf2b5bdd07bbeb 100644 (file)
@@ -1,5 +1,5 @@
 /* Menu support for GNU Emacs on the Microsoft W32 API.
-   Copyright (C) 1986, 88, 93, 94, 96, 98, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1986,88,93,94,96,98,1999,2003  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -45,7 +45,6 @@ Boston, MA 02111-1307, USA.  */
 
 #include "dispextern.h"
 
-#undef HAVE_MULTILINGUAL_MENU
 #undef HAVE_DIALOGS /* TODO: Implement native dialogs.  */
 
 /******************************************************************/
@@ -66,10 +65,12 @@ enum button_type
 typedef struct _widget_value
 {
   /* name of widget */
+  Lisp_Object   lname;
   char*                name;
   /* value (meaning depend on widget type) */
   char*                value;
-  /* keyboard equivalent. no implications for XtTranslations */ 
+  /* keyboard equivalent. no implications for XtTranslations */
+  Lisp_Object   lkey;
   char*                key;
   /* Help string or nil if none.
      GC finds this string through the frame's menu_bar_vector
@@ -136,24 +137,22 @@ typedef BOOL (WINAPI * GetMenuItemInfoA_Proc) (
     IN HMENU,
     IN UINT,
     IN BOOL,
-    IN OUT LPMENUITEMINFOA
-    );
+    IN OUT LPMENUITEMINFOA);
 typedef BOOL (WINAPI * SetMenuItemInfoA_Proc) (
     IN HMENU,
     IN UINT,
     IN BOOL,
-    IN LPCMENUITEMINFOA
-    );
+    IN LPCMENUITEMINFOA);
 
-GetMenuItemInfoA_Proc get_menu_item_info=NULL;
-SetMenuItemInfoA_Proc set_menu_item_info=NULL;
+GetMenuItemInfoA_Proc get_menu_item_info = NULL;
+SetMenuItemInfoA_Proc set_menu_item_info = NULL;
+AppendMenuW_Proc unicode_append_menu = NULL;
 
 Lisp_Object Vmenu_updating_frame;
 
 Lisp_Object Qdebug_on_next_call;
 
 extern Lisp_Object Qmenu_bar;
-extern Lisp_Object Qmouse_click, Qevent_kind;
 
 extern Lisp_Object QCtoggle, QCradio;
 
@@ -500,7 +499,7 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
 \f
 /* This is a subroutine of single_keymap_panes that handles one
    keymap entry.
-   KEY is a key in a keymap and ITEM is its binding. 
+   KEY is a key in a keymap and ITEM is its binding.
    PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
    separate panes.
    If NOTREAL is nonzero, only check for equivalent key bindings, don't
@@ -516,7 +515,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
   Lisp_Object map, item_string, enabled;
   struct gcpro gcpro1, gcpro2;
   int res;
-  
+
   /* Parse the menu item and leave the result in item_properties.  */
   GCPRO2 (key, item);
   res = parse_menu_item (item, notreal, 0);
@@ -525,7 +524,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
     return;                    /* Not a menu item.  */
 
   map = AREF (item_properties, ITEM_PROPERTY_MAP);
-  
+
   if (notreal)
     {
       /* We don't want to make a menu, just traverse the keymaps to
@@ -536,7 +535,7 @@ single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth)
     }
 
   enabled = AREF (item_properties, ITEM_PROPERTY_ENABLE);
-  item_string = AREF (item_properties, ITEM_PROPERTY_NAME); 
+  item_string = AREF (item_properties, ITEM_PROPERTY_NAME);
 
   if (!NILP (map) && SREF (item_string, 0) == '@')
     {
@@ -725,10 +724,8 @@ cached information about equivalent key sequences.  */)
          CHECK_LIVE_WINDOW (window);
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-         xpos = (FONT_WIDTH (FRAME_FONT (f))
-                 * XFASTINT (XWINDOW (window)->left));
-         ypos = (FRAME_LINE_HEIGHT (f)
-                 * XFASTINT (XWINDOW (window)->top));
+         xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
+         ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -811,7 +808,7 @@ cached information about equivalent key sequences.  */)
 
       keymaps = 0;
     }
-  
+
   if (NILP (position))
     {
       discard_menu_items ();
@@ -828,8 +825,8 @@ cached information about equivalent key sequences.  */)
       discard_menu_items ();
       UNGCPRO;
       return Qnil;
-    }    
-  
+    }
+
   /* Display them in a menu.  */
   BLOCK_INPUT;
 
@@ -974,7 +971,7 @@ on the left of the dialog box and all following items on the right.
    But first we recompute the menu bar contents (the whole tree).
 
    This way we can safely execute Lisp code.  */
-   
+
 void
 x_activate_menubar (f)
      FRAME_PTR f;
@@ -1037,6 +1034,7 @@ menubar_selection_callback (FRAME_PTR f, void * client_data)
              int j;
              struct input_event buf;
              Lisp_Object frame;
+             EVENT_INIT (buf);
 
              XSETFRAME (frame, f);
              buf.kind = MENU_BAR_EVENT;
@@ -1105,7 +1103,7 @@ free_menubar_widget_value_tree (wv)
      widget_value *wv;
 {
   if (! wv) return;
-  
+
   wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
 
   if (wv->contents && (wv->contents != (widget_value*)1))
@@ -1171,7 +1169,7 @@ parse_single_submenu (item_key, item_name, maps)
                               item_key, 0, 10);
        }
     }
-  
+
   return top_level_items;
 }
 
@@ -1200,7 +1198,7 @@ digest_single_submenu (start, end, top_level_items)
   first_wv = wv;
   save_wv = 0;
   prev_wv = 0;
+
   /* Loop over all panes and items made by the preceding call
      to parse_single_submenu and construct a tree of widget_value objects.
      Ignore the panes and items used by previous calls to
@@ -1237,13 +1235,17 @@ digest_single_submenu (start, end, top_level_items)
          pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
 
-#ifndef HAVE_MULTILINGUAL_MENU
-         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+         if (STRINGP (pane_name))
            {
-             pane_name = ENCODE_SYSTEM (pane_name);
+             if (unicode_append_menu)
+               /* Encode as UTF-8 for now.  */
+               pane_name = ENCODE_UTF_8 (pane_name);
+             else if (STRING_MULTIBYTE (pane_name))
+               pane_name = ENCODE_SYSTEM (pane_name);
+
              ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
            }
-#endif
+
          pane_string = (NILP (pane_name)
                         ? "" : (char *) SDATA (pane_name));
          /* If there is just one top-level pane, put all its items directly
@@ -1261,12 +1263,9 @@ digest_single_submenu (start, end, top_level_items)
                save_wv->next = wv;
              else
                first_wv->contents = wv;
-             wv->name = pane_string;
-             /* Ignore the @ that means "separate pane".
-                This is a kludge, but this isn't worth more time.  */
-             if (!NILP (prefix) && wv->name[0] == '@')
-               wv->name++;
-             wv->value = 0;
+             wv->lname = pane_name;
+             /* Set value to 1 so update_submenu_strings can handle '@'  */
+             wv->value = (char *) 1;
              wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
              wv->help = Qnil;
@@ -1289,10 +1288,13 @@ digest_single_submenu (start, end, top_level_items)
          selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
          help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
 
-#ifndef HAVE_MULTILINGUAL_MENU
-         if (STRING_MULTIBYTE (item_name))
+         if (STRINGP (item_name))
            {
-             item_name = ENCODE_SYSTEM (item_name);
+             if (unicode_append_menu)
+               item_name = ENCODE_UTF_8 (item_name);
+             else if (STRING_MULTIBYTE (item_name))
+               item_name = ENCODE_SYSTEM (item_name);
+
              ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
            }
 
@@ -1301,17 +1303,16 @@ digest_single_submenu (start, end, top_level_items)
              descrip = ENCODE_SYSTEM (descrip);
              ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
            }
-#endif /* not HAVE_MULTILINGUAL_MENU */
 
          wv = xmalloc_widget_value ();
-         if (prev_wv) 
+         if (prev_wv)
            prev_wv->next = wv;
          else
            save_wv->contents = wv;
 
-         wv->name = (char *) SDATA (item_name);
+         wv->lname = item_name;
          if (!NILP (descrip))
-           wv->key = (char *) SDATA (descrip);
+           wv->lkey = descrip;
          wv->value = 0;
          /* The EMACS_INT cast avoids a warning.  There's no problem
             as long as pointers have enough bits to hold small integers.  */
@@ -1350,6 +1351,43 @@ digest_single_submenu (start, end, top_level_items)
 
   return first_wv;
 }
+
+
+/* Walk through the widget_value tree starting at FIRST_WV and update
+   the char * pointers from the corresponding lisp values.
+   We do this after building the whole tree, since GC may happen while the
+   tree is constructed, and small strings are relocated.  So we must wait
+   until no GC can happen before storing pointers into lisp values.  */
+static void
+update_submenu_strings (first_wv)
+     widget_value *first_wv;
+{
+  widget_value *wv;
+
+  for (wv = first_wv; wv; wv = wv->next)
+    {
+      if (wv->lname && ! NILP (wv->lname))
+        {
+          wv->name = SDATA (wv->lname);
+
+          /* Ignore the @ that means "separate pane".
+             This is a kludge, but this isn't worth more time.  */
+          if (wv->value == (char *)1)
+            {
+              if (wv->name[0] == '@')
+               wv->name++;
+              wv->value = 0;
+            }
+        }
+
+      if (wv->lkey && ! NILP (wv->lkey))
+        wv->key = SDATA (wv->lkey);
+
+      if (wv->contents)
+        update_submenu_strings (wv->contents);
+    }
+}
+
 \f
 /* Set the contents of the menubar widgets of frame F.
    The argument FIRST_TIME is currently ignored;
@@ -1477,7 +1515,7 @@ set_frame_menubar (f, first_time, deep_p)
          menu_items_n_panes = submenu_n_panes[i];
          wv = digest_single_submenu (submenu_start[i], submenu_end[i],
                                      submenu_top_level_items[i]);
-         if (prev_wv) 
+         if (prev_wv)
            prev_wv->next = wv;
          else
            first_wv->contents = wv;
@@ -1518,6 +1556,7 @@ set_frame_menubar (f, first_time, deep_p)
          if (NILP (string))
            break;
          wv->name = (char *) SDATA (string);
+         update_submenu_strings (wv->contents);
          wv = wv->next;
        }
 
@@ -1559,7 +1598,7 @@ set_frame_menubar (f, first_time, deep_p)
             This value just has to be different from small integers.  */
          wv->call_data = (void *) (EMACS_INT) (-1);
 
-         if (prev_wv) 
+         if (prev_wv)
            prev_wv->next = wv;
          else
            first_wv->contents = wv;
@@ -1595,13 +1634,13 @@ set_frame_menubar (f, first_time, deep_p)
 
     f->output_data.w32->menubar_widget = menubar_widget;
     SetMenu (FRAME_W32_WINDOW (f), f->output_data.w32->menubar_widget);
-    /* Causes flicker when menu bar is updated 
+    /* Causes flicker when menu bar is updated
     DrawMenuBar (FRAME_W32_WINDOW (f)); */
 
     /* 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_WIDTH (f), FRAME_HEIGHT (f));
+      x_set_window_size (f, 0, FRAME_COLS (f), FRAME_LINES (f));
   }
 
   UNBLOCK_INPUT;
@@ -1697,7 +1736,7 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
   wv->help = Qnil;
   first_wv = wv;
   first_pane = 1;
+
   /* Loop over all panes and items, filling in the tree.  */
   i = 0;
   while (i < menu_items_used)
@@ -1731,13 +1770,17 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
          char *pane_string;
          pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
-#ifndef HAVE_MULTILINGUAL_MENU
-         if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+
+         if (STRINGP (pane_name))
            {
-             pane_name = ENCODE_SYSTEM (pane_name);
+             if (unicode_append_menu)
+               pane_name = ENCODE_UTF_8 (pane_name);
+             else if (STRING_MULTIBYTE (pane_name))
+               pane_name = ENCODE_SYSTEM (pane_name);
+
              ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
            }
-#endif
+
          pane_string = (NILP (pane_name)
                         ? "" : (char *) SDATA (pane_name));
          /* If there is just one top-level pane, put all its items directly
@@ -1786,23 +1829,26 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
          selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
           help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
 
-#ifndef HAVE_MULTILINGUAL_MENU
-          if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+          if (STRINGP (item_name))
            {
-             item_name = ENCODE_SYSTEM (item_name);
+             if (unicode_append_menu)
+               item_name = ENCODE_UTF_8 (item_name);
+             else if (STRING_MULTIBYTE (item_name))
+               item_name = ENCODE_SYSTEM (item_name);
+
              ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
            }
-          if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+
+         if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
             {
              descrip = ENCODE_SYSTEM (descrip);
              ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
            }
-#endif /* not HAVE_MULTILINGUAL_MENU */
 
          wv = xmalloc_widget_value ();
-         if (prev_wv) 
+         if (prev_wv)
            prev_wv->next = wv;
-         else 
+         else
            save_wv->contents = wv;
          wv->name = (char *) SDATA (item_name);
          if (!NILP (descrip))
@@ -1846,10 +1892,11 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
       wv_sep->next = first_wv->contents;
       wv_sep->help = Qnil;
 
-#ifndef HAVE_MULTILINGUAL_MENU
-      if (STRING_MULTIBYTE (title))
+      if (unicode_append_menu)
+       title = ENCODE_UTF_8 (title);
+      else if (STRING_MULTIBYTE (title))
        title = ENCODE_SYSTEM (title);
-#endif
+
       wv_title->name = (char *) SDATA (title);
       wv_title->enabled = TRUE;
       wv_title->title = TRUE;
@@ -1872,7 +1919,7 @@ w32_menu_show (f, x, y, for_click, keymaps, title, error)
   menu_item_selection = 0;
 
   /* Display the menu.  */
-  menu_item_selection = SendMessage (FRAME_W32_WINDOW (f), 
+  menu_item_selection = SendMessage (FRAME_W32_WINDOW (f),
                                     WM_EMACS_TRACKPOPUPMENU,
                                     (WPARAM)menu, (LPARAM)&pos);
 
@@ -1981,7 +2028,7 @@ w32_dialog_show (f, keymaps, title, error)
     pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
     prefix = AREF (menu_items, MENU_ITEMS_PANE_PREFIX);
     pane_string = (NILP (pane_name)
-                  ? "" : (char *) SDATA (pane_name));  
+                  ? "" : (char *) SDATA (pane_name));
     prev_wv = xmalloc_widget_value ();
     prev_wv->value = pane_string;
     if (keymaps && !NILP (prefix))
@@ -1990,12 +2037,12 @@ w32_dialog_show (f, keymaps, title, error)
     prev_wv->name = "message";
     prev_wv->help = Qnil;
     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, help;
 
@@ -2003,7 +2050,7 @@ w32_dialog_show (f, keymaps, title, error)
        enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
        descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
         help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
-       
+
        if (NILP (item_name))
          {
            free_menubar_widget_value_tree (first_wv);
@@ -2087,7 +2134,7 @@ w32_dialog_show (f, keymaps, title, error)
   /* Process events that apply to the menu.  */
   popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
 
-  lw_destroy_all_widgets (dialog_id); 
+  lw_destroy_all_widgets (dialog_id);
 
   /* Find the selected item, and its pane, to return
      the proper value.  */
@@ -2152,6 +2199,46 @@ add_left_right_boundary (HMENU menu)
   return AppendMenu (menu, MF_MENUBARBREAK, 0, NULL);
 }
 
+/* UTF8: 0xxxxxxx, 110xxxxx 10xxxxxx, 1110xxxx, 10xxxxxx, 10xxxxxx */
+static void
+utf8to16 (unsigned char * src, int len, WCHAR * dest)
+{
+  while (len > 0)
+    {
+      int utf16;
+      if (*src < 0x80)
+       {
+         *dest = (WCHAR) *src;
+         dest++; src++; len--;
+       }
+      /* Since we might get >3 byte sequences which we don't handle, ignore the extra parts.  */
+      else if (*src < 0xC0)
+       {
+         src++; len--;
+       }
+      /* 2 char UTF-8 sequence.  */
+      else if (*src <  0xE0)
+       {
+         *dest = (WCHAR) (((*src & 0x1f) << 6)
+                          | (*(src + 1) & 0x3f));
+         src += 2; len -= 2; dest++;
+       }
+      else if (*src < 0xF0)
+       {
+         *dest = (WCHAR) (((*src & 0x0f) << 12)
+                          | ((*(src + 1) & 0x3f) << 6)
+                          | (*(src + 2) & 0x3f));
+         src += 3; len -= 3; dest++;
+       }
+      else /* Not encodable. Insert Unicode Substitution char.  */
+       {
+         *dest = (WCHAR) 0xfffd;
+         src++; len--; dest++;
+       }
+    }
+  *dest = 0;
+}
+
 static int
 add_menu_item (HMENU menu, widget_value *wv, HMENU item)
 {
@@ -2164,7 +2251,7 @@ add_menu_item (HMENU menu, widget_value *wv, HMENU item)
       fuFlags = MF_SEPARATOR;
       out_string = NULL;
     }
-  else 
+  else
     {
       if (wv->enabled)
        fuFlags = MF_STRING;
@@ -2208,11 +2295,32 @@ add_menu_item (HMENU menu, widget_value *wv, HMENU item)
        fuFlags |= MF_UNCHECKED;
     }
 
-  return_value =
-    AppendMenu (menu,
-                fuFlags,
-                item != NULL ? (UINT) item : (UINT) wv->call_data,
-                out_string );
+  if (unicode_append_menu && out_string)
+    {
+      /* Convert out_string from UTF-8 to UTF-16-LE.  */
+      int utf8_len = strlen (out_string);
+      WCHAR * utf16_string;
+      if (fuFlags & MF_OWNERDRAW)
+       utf16_string = local_alloc ((utf8_len + 1) * sizeof (WCHAR));
+      else
+       utf16_string = alloca ((utf8_len + 1) * sizeof (WCHAR));
+
+      utf8to16 (out_string, utf8_len, utf16_string);
+      return_value = unicode_append_menu (menu, fuFlags,
+                                         item != NULL ? (UINT) item
+                                           : (UINT) wv->call_data,
+                                         utf16_string);
+      if (fuFlags & MF_OWNERDRAW)
+       local_free (out_string);
+    }
+  else
+    {
+      return_value =
+       AppendMenu (menu,
+                   fuFlags,
+                   item != NULL ? (UINT) item : (UINT) wv->call_data,
+                   out_string );
+    }
 
   /* This must be done after the menu item is created.  */
   if (!wv->title && wv->call_data != 0)
@@ -2227,9 +2335,12 @@ add_menu_item (HMENU menu, widget_value *wv, HMENU item)
          /* Set help string for menu item.  Leave it as a Lisp_Object
             until it is ready to be displayed, since GC can happen while
             menus are active.  */
-         if (wv->help)
-           info.dwItemData = (DWORD) wv->help;
-
+         if (!NILP (wv->help))
+#ifdef USE_LISP_UNION_TYPE
+           info.dwItemData = (DWORD) (wv->help).i;
+#else
+           info.dwItemData = (DWORD) (wv->help);
+#endif
          if (wv->button_type == BUTTON_TYPE_RADIO)
            {
              /* CheckMenuRadioItem allows us to differentiate TOGGLE and
@@ -2297,7 +2408,7 @@ w32_menu_display_help (HWND owner, HMENU menu, UINT item, UINT flags)
       struct frame *f = x_window_to_frame (&one_w32_display_info, owner);
       Lisp_Object frame, help;
 
-      // No help echo on owner-draw menu items.
+      /* No help echo on owner-draw menu items.  */
       if (flags & MF_OWNERDRAW || flags & MF_POPUP)
        help = Qnil;
       else
@@ -2309,7 +2420,12 @@ w32_menu_display_help (HWND owner, HMENU menu, UINT item, UINT flags)
          info.fMask = MIIM_DATA;
          get_menu_item_info (menu, item, FALSE, &info);
 
+#ifdef USE_LISP_UNION_TYPE
+         help = info.dwItemData ? (Lisp_Object) ((EMACS_INT) info.dwItemData)
+                                : Qnil;
+#else
          help = info.dwItemData ? (Lisp_Object) info.dwItemData : Qnil;
+#endif
        }
 
       /* Store the help echo in the keyboard buffer as the X toolkit
@@ -2416,4 +2532,8 @@ void globals_of_w32menu ()
   HMODULE user32 = GetModuleHandle ("user32.dll");
   get_menu_item_info = (GetMenuItemInfoA_Proc) GetProcAddress (user32, "GetMenuItemInfoA");
   set_menu_item_info = (SetMenuItemInfoA_Proc) GetProcAddress (user32, "SetMenuItemInfoA");
+  unicode_append_menu = (AppendMenuW_Proc) GetProcAddress (user32, "AppendMenuW");
 }
+
+/* arch-tag: 0eaed431-bb4e-4aac-a527-95a1b4f1fed0
+   (do not change this comment) */