X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/90022f5a3e142d08696bd4d73ac26c308aa762fd..ff6daed30415db8a1110e11f67f5d61ef02f04a5:/src/w32menu.c diff --git a/src/w32menu.c b/src/w32menu.c index 872cb12e99..75c55ef4f8 100644 --- a/src/w32menu.c +++ b/src/w32menu.c @@ -1,5 +1,6 @@ /* 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, 1988, 1993, 1994, 1996, 1998, 1999, 2002, 2003, + 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -15,8 +16,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ #include #include @@ -45,7 +46,6 @@ Boston, MA 02111-1307, USA. */ #include "dispextern.h" -#undef HAVE_MULTILINGUAL_MENU #undef HAVE_DIALOGS /* TODO: Implement native dialogs. */ /******************************************************************/ @@ -66,10 +66,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 */ + Lisp_Object lkey; char* key; /* Help string or nil if none. GC finds this string through the frame's menu_bar_vector @@ -136,24 +138,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; @@ -846,7 +846,7 @@ cached information about equivalent key sequences. */) #ifdef HAVE_MENUS -DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0, +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. @@ -861,9 +861,12 @@ 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.) */) - (position, contents) - Lisp_Object position, contents; +\(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". */) + (position, contents, header) + Lisp_Object position, contents, header; { FRAME_PTR f = NULL; Lisp_Object window; @@ -950,7 +953,7 @@ on the left of the dialog box and all following items on the right. /* Display them in a dialog box. */ BLOCK_INPUT; - selection = w32_dialog_show (f, 0, title, &error_name); + selection = w32_dialog_show (f, 0, title, header, &error_name); UNBLOCK_INPUT; discard_menu_items (); @@ -1035,6 +1038,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; @@ -1235,13 +1239,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 @@ -1259,12 +1267,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; @@ -1287,10 +1292,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); } @@ -1299,7 +1307,6 @@ 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) @@ -1307,9 +1314,9 @@ digest_single_submenu (start, end, top_level_items) 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. */ @@ -1348,6 +1355,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); + } +} + /* Set the contents of the menubar widgets of frame F. The argument FIRST_TIME is currently ignored; @@ -1400,7 +1444,8 @@ set_frame_menubar (f, first_time, deep_p) because it is not reentrant. */ specbind (Qdebug_on_next_call, Qnil); - record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil)); + record_unwind_save_match_data (); + if (NILP (Voverriding_local_map_menu_flag)) { specbind (Qoverriding_terminal_local_map, Qnil); @@ -1516,6 +1561,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; } @@ -1729,13 +1775,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 @@ -1784,18 +1834,21 @@ 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) @@ -1844,10 +1897,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; @@ -1946,10 +2000,10 @@ static char * button_names [] = { "button6", "button7", "button8", "button9", "button10" }; static Lisp_Object -w32_dialog_show (f, keymaps, title, error) +w32_dialog_show (f, keymaps, title, header, error) FRAME_PTR f; int keymaps; - Lisp_Object title; + Lisp_Object title, header; char **error; { int i, nb_buttons=0; @@ -2050,11 +2104,17 @@ w32_dialog_show (f, keymaps, title, error) wv->name = dialog_name; wv->help = Qnil; + /* Frame title: 'Q' = Question, 'I' = Information. + Can also have 'E' = Error if, one day, we want + a popup for errors. */ + if (NILP(header)) + dialog_name[0] = 'Q'; + else + dialog_name[0] = 'I'; + /* Dialog boxes use a really stupid name encoding which specifies how many buttons to use - and how many buttons are on the right. - The Q means something also. */ - dialog_name[0] = 'Q'; + and how many buttons are on the right. */ dialog_name[1] = '0' + nb_buttons; dialog_name[2] = 'B'; dialog_name[3] = 'R'; @@ -2150,6 +2210,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) { @@ -2206,11 +2306,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) @@ -2225,9 +2346,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 @@ -2295,8 +2419,11 @@ 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. - if (flags & MF_OWNERDRAW || flags & MF_POPUP) + /* No help echo on owner-draw menu items, or when the keyboard is used + to navigate the menus, since tooltips are distracting if they pop + up elsewhere. */ + if (flags & MF_OWNERDRAW || flags & MF_POPUP + || !(flags & MF_MOUSESELECT)) help = Qnil; else { @@ -2307,7 +2434,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 @@ -2414,4 +2546,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) */