changeset 32491:32c9b7396a75 v9.0.1577

patch 9.0.1577: MS-Windows: context menu translations may be wrong Commit: https://github.com/vim/vim/commit/1271572a35ae215fa023d97db9896c5745591024 Author: K.Takata <kentkt@csc.jp> Date: Thu May 25 16:43:27 2023 +0100 patch 9.0.1577: MS-Windows: context menu translations may be wrong Problem: MS-Windows: context menu translations may be wrong. Solution: Set the encoding before using gettext(). (Ken Takata, closes #12441, closes #12431)
author Bram Moolenaar <Bram@vim.org>
date Thu, 25 May 2023 17:45:05 +0200
parents 4f54d3384e48
children 8064be0111ef
files src/GvimExt/Make_ming.mak src/GvimExt/Make_mvc.mak src/GvimExt/gvimext.cpp src/GvimExt/gvimext.h src/version.c
diffstat 5 files changed, 168 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/src/GvimExt/Make_ming.mak
+++ b/src/GvimExt/Make_ming.mak
@@ -43,9 +43,9 @@ else
 DEL = del
 endif
 endif
-# Set the default $(WINVER) to make it work with WinXP.
+# Set the default $(WINVER) to make it work with Windows 7.
 ifndef WINVER
-WINVER = 0x0501
+WINVER = 0x0601
 endif
 CXX := $(CROSS_COMPILE)g++
 WINDRES := $(CROSS_COMPILE)windres
--- a/src/GvimExt/Make_mvc.mak
+++ b/src/GvimExt/Make_mvc.mak
@@ -8,10 +8,11 @@
 TARGETOS = WINNT
 
 !ifndef APPVER
-APPVER = 5.01
+APPVER = 6.01
 !endif
+# Set the default $(WINVER) to make it work with Windows 7.
 !ifndef WINVER
-WINVER = 0x0501
+WINVER = 0x0601
 !endif
 
 !if "$(DEBUG)" != "yes"
@@ -40,9 +41,9 @@ CPU = i386
 !endif
 
 !ifdef SDK_INCLUDE_DIR
-!include $(SDK_INCLUDE_DIR)\Win32.mak
+! include $(SDK_INCLUDE_DIR)\Win32.mak
 !elseif "$(USE_WIN32MAK)"=="yes"
-!include <Win32.mak>
+! include <Win32.mak>
 !else
 cc = cl
 link = link
--- a/src/GvimExt/gvimext.cpp
+++ b/src/GvimExt/gvimext.cpp
@@ -130,33 +130,48 @@ getRuntimeDir(char *buf)
     }
 }
 
-HBITMAP IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height)
+    WCHAR *
+utf8_to_utf16(const char *s)
 {
-	HDC hDC = GetDC(NULL);
-	HDC hMemDC = CreateCompatibleDC(hDC);
-	HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height);
-	HBITMAP hResultBmp = NULL;
-	HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp);
+    int size = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
+    WCHAR *buf = (WCHAR *)malloc(size * sizeof(WCHAR));
+    MultiByteToWideChar(CP_UTF8, 0, s, -1, buf, size);
+    return buf;
+}
 
-	DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL);
-
-	hResultBmp = hMemBmp;
-	hMemBmp = NULL;
+    HBITMAP
+IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height)
+{
+    HDC hDC = GetDC(NULL);
+    HDC hMemDC = CreateCompatibleDC(hDC);
+    HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height);
+    HBITMAP hResultBmp = NULL;
+    HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp);
 
-	SelectObject(hMemDC, hOrgBMP);
-	DeleteDC(hMemDC);
-	ReleaseDC(NULL, hDC);
-	DestroyIcon(hIcon);
-	return hResultBmp;
+    DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL);
+
+    hResultBmp = hMemBmp;
+    hMemBmp = NULL;
+
+    SelectObject(hMemDC, hOrgBMP);
+    DeleteDC(hMemDC);
+    ReleaseDC(NULL, hDC);
+    DestroyIcon(hIcon);
+    return hResultBmp;
 }
 
 //
 // GETTEXT: translated messages and menu entries
 //
 #ifndef FEAT_GETTEXT
-# define _(x)  x
+# define _(x)	    x
+# define W_impl(x) _wcsdup(L##x)
+# define W(x)	    W_impl(x)
+# define set_gettext_codeset()	    NULL
+# define restore_gettext_codeset(x)
 #else
-# define _(x)  (*dyn_libintl_gettext)(x)
+# define _(x)	    (*dyn_libintl_gettext)(x)
+# define W(x)	    utf8_to_utf16(x)
 # define VIMPACKAGE "vim"
 # ifndef GETTEXT_DLL
 #  define GETTEXT_DLL "libintl.dll"
@@ -167,6 +182,7 @@ HBITMAP IconToBitmap(HICON hIcon, HBRUSH
 static char *null_libintl_gettext(const char *);
 static char *null_libintl_textdomain(const char *);
 static char *null_libintl_bindtextdomain(const char *, const char *);
+static char *null_libintl_bind_textdomain_codeset(const char *, const char *);
 static int dyn_libintl_init(char *dir);
 static void dyn_libintl_end(void);
 
@@ -175,6 +191,8 @@ static char *(*dyn_libintl_gettext)(cons
 static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain;
 static char *(*dyn_libintl_bindtextdomain)(const char *, const char *)
 						= null_libintl_bindtextdomain;
+static char *(*dyn_libintl_bind_textdomain_codeset)(const char *, const char *)
+				       = null_libintl_bind_textdomain_codeset;
 
 //
 // Attempt to load libintl.dll.  If it doesn't work, use dummy functions.
@@ -194,6 +212,7 @@ dyn_libintl_init(char *dir)
 	{(char *)"gettext",		(FARPROC*)&dyn_libintl_gettext},
 	{(char *)"textdomain",		(FARPROC*)&dyn_libintl_textdomain},
 	{(char *)"bindtextdomain",	(FARPROC*)&dyn_libintl_bindtextdomain},
+	{(char *)"bind_textdomain_codeset", (FARPROC*)&dyn_libintl_bind_textdomain_codeset},
 	{NULL, NULL}
     };
     DWORD	len, len2;
@@ -254,6 +273,7 @@ dyn_libintl_end(void)
     dyn_libintl_gettext		= null_libintl_gettext;
     dyn_libintl_textdomain	= null_libintl_textdomain;
     dyn_libintl_bindtextdomain	= null_libintl_bindtextdomain;
+    dyn_libintl_bind_textdomain_codeset	= null_libintl_bind_textdomain_codeset;
 }
 
     static char *
@@ -263,13 +283,19 @@ null_libintl_gettext(const char *msgid)
 }
 
     static char *
+null_libintl_textdomain(const char * /* domainname */)
+{
+    return NULL;
+}
+
+    static char *
 null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */)
 {
     return NULL;
 }
 
     static char *
-null_libintl_textdomain(const char*  /* domainname */)
+null_libintl_bind_textdomain_codeset(const char * /* domainname */, const char * /* codeset */)
 {
     return NULL;
 }
@@ -304,6 +330,29 @@ dyn_gettext_free(void)
 {
     dyn_libintl_end();
 }
+
+//
+// Use UTF-8 for gettext. Returns previous codeset.
+//
+    static char *
+set_gettext_codeset(void)
+{
+    char *prev = dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, NULL);
+    prev = _strdup((prev != NULL) ? prev : "char");
+    dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, "utf-8");
+
+    return prev;
+}
+
+//
+// Restore previous codeset for gettext.
+//
+    static void
+restore_gettext_codeset(char *prev)
+{
+    dyn_libintl_bind_textdomain_codeset(VIMPACKAGE, prev);
+    free(prev);
+}
 #endif // FEAT_GETTEXT
 
 //
@@ -583,7 +632,7 @@ STDMETHODIMP CShellExt::QueryContextMenu
 
     hres = m_pDataObj->GetData(&fmte, &medium);
     if (medium.hGlobal)
-	cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0);
+	cbFiles = DragQueryFileW((HDROP)medium.hGlobal, (UINT)-1, 0, 0);
 
     // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
 
@@ -607,11 +656,14 @@ STDMETHODIMP CShellExt::QueryContextMenu
 	RegCloseKey(keyhandle);
     }
 
+    // Use UTF-8 for gettext.
+    char *prev = set_gettext_codeset();
+
     // Retrieve all the vim instances, unless disabled.
     if (showExisting)
 	EnumWindows(EnumWindowsProc, (LPARAM)this);
 
-    MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
+    MENUITEMINFOW mii = { sizeof(MENUITEMINFOW) };
     mii.fMask = MIIM_STRING | MIIM_ID;
     if (showIcons)
     {
@@ -622,22 +674,25 @@ STDMETHODIMP CShellExt::QueryContextMenu
     if (cbFiles > 1)
     {
 	mii.wID = idCmd++;
-	mii.dwTypeData = _("Edit with Vim using &tabpages");
-	mii.cch = lstrlen(mii.dwTypeData);
-	InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
+	mii.dwTypeData = W(_("Edit with Vim using &tabpages"));
+	mii.cch = wcslen(mii.dwTypeData);
+	InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
+	free(mii.dwTypeData);
 
 	mii.wID = idCmd++;
-	mii.dwTypeData = _("Edit with single &Vim");
-	mii.cch = lstrlen(mii.dwTypeData);
-	InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
+	mii.dwTypeData = W(_("Edit with single &Vim"));
+	mii.cch = wcslen(mii.dwTypeData);
+	InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
+	free(mii.dwTypeData);
 
 	if (cbFiles <= 4)
 	{
 	    // Can edit up to 4 files in diff mode
 	    mii.wID = idCmd++;
-	    mii.dwTypeData = _("Diff with Vim");
-	    mii.cch = lstrlen(mii.dwTypeData);
-	    InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
+	    mii.dwTypeData = W(_("Diff with Vim"));
+	    mii.cch = wcslen(mii.dwTypeData);
+	    InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
+	    free(mii.dwTypeData);
 	    m_edit_existing_off = 3;
 	}
 	else
@@ -647,9 +702,10 @@ STDMETHODIMP CShellExt::QueryContextMenu
     else
     {
 	mii.wID = idCmd++;
-	mii.dwTypeData = _("Edit with &Vim");
-	mii.cch = lstrlen(mii.dwTypeData);
-	InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
+	mii.dwTypeData = W(_("Edit with &Vim"));
+	mii.cch = wcslen(mii.dwTypeData);
+	InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
+	free(mii.dwTypeData);
 	m_edit_existing_off = 1;
     }
 
@@ -659,46 +715,49 @@ STDMETHODIMP CShellExt::QueryContextMenu
 	hSubMenu = CreatePopupMenu();
 	mii.fMask |= MIIM_SUBMENU;
 	mii.wID = idCmd;
-	mii.dwTypeData = _("Edit with existing Vim");
-	mii.cch = lstrlen(mii.dwTypeData);
+	mii.dwTypeData = W(_("Edit with existing Vim"));
+	mii.cch = wcslen(mii.dwTypeData);
 	mii.hSubMenu = hSubMenu;
-	InsertMenuItem(hMenu, indexMenu++, TRUE, &mii);
+	InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
+	free(mii.dwTypeData);
 	mii.fMask = mii.fMask & ~MIIM_SUBMENU;
 	mii.hSubMenu = NULL;
     }
     // Now display all the vim instances
     for (int i = 0; i < m_cntOfHWnd; i++)
     {
-	char title[BUFSIZE];
-	char temp[BUFSIZE];
+	WCHAR title[BUFSIZE];
+	WCHAR temp[BUFSIZE];
 	int index;
 	HMENU hmenu;
 
 	// Obtain window title, continue if can not
-	if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0)
+	if (GetWindowTextW(m_hWnd[i], title, BUFSIZE - 1) == 0)
 	    continue;
 	// Truncate the title before the path, keep the file name
-	char *pos = strchr(title, '(');
+	WCHAR *pos = wcschr(title, L'(');
 	if (pos != NULL)
 	{
-	    if (pos > title && pos[-1] == ' ')
+	    if (pos > title && pos[-1] == L' ')
 		--pos;
 	    *pos = 0;
 	}
 	// Now concatenate
 	if (m_cntOfHWnd > 1)
-	    temp[0] = '\0';
+	    temp[0] = L'\0';
 	else
 	{
-	    strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1);
-	    temp[BUFSIZE - 1] = '\0';
+	    WCHAR *s = W(_("Edit with existing Vim - "));
+	    wcsncpy(temp, s, BUFSIZE - 1);
+	    temp[BUFSIZE - 1] = L'\0';
+	    free(s);
 	}
-	strncat(temp, title, BUFSIZE - 1 - strlen(temp));
-	temp[BUFSIZE - 1] = '\0';
+	wcsncat(temp, title, BUFSIZE - 1 - wcslen(temp));
+	temp[BUFSIZE - 1] = L'\0';
 
 	mii.wID = idCmd++;
 	mii.dwTypeData = temp;
-	mii.cch = lstrlen(mii.dwTypeData);
+	mii.cch = wcslen(mii.dwTypeData);
 	if (m_cntOfHWnd > 1)
 	{
 	    hmenu = hSubMenu;
@@ -709,10 +768,13 @@ STDMETHODIMP CShellExt::QueryContextMenu
 	    hmenu = hMenu;
 	    index = indexMenu++;
 	}
-	InsertMenuItem(hmenu, index, TRUE, &mii);
+	InsertMenuItemW(hmenu, index, TRUE, &mii);
     }
     // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
 
+    // Restore previous codeset.
+    restore_gettext_codeset(prev);
+
     // Must return number of menu items we added.
     return ResultFromShort(idCmd-idCmdFirst);
 }
@@ -819,8 +881,16 @@ STDMETHODIMP CShellExt::GetCommandString
 					 LPSTR pszName,
 					 UINT cchMax)
 {
-    if (uFlags == GCS_HELPTEXT && cchMax > 35)
-	lstrcpy(pszName, _("Edits the selected file(s) with Vim"));
+    // Use UTF-8 for gettext.
+    char *prev = set_gettext_codeset();
+
+    WCHAR *s = W(_("Edits the selected file(s) with Vim"));
+    if (uFlags == GCS_HELPTEXTW && cchMax > wcslen(s))
+	wcscpy((WCHAR *)pszName, s);
+    free(s);
+
+    // Restore previous codeset.
+    restore_gettext_codeset(prev);
 
     return NOERROR;
 }
@@ -831,7 +901,8 @@ BOOL CALLBACK CShellExt::EnumWindowsProc
 
     // First do a bunch of check
     // No invisible window
-    if (!IsWindowVisible(hWnd)) return TRUE;
+    if (!IsWindowVisible(hWnd))
+	return TRUE;
     // No child window ???
     // if (GetParent(hWnd)) return TRUE;
     // Class name should be Vim, if failed to get class name, return
@@ -842,7 +913,8 @@ BOOL CALLBACK CShellExt::EnumWindowsProc
 	return TRUE;
     // First check if the number of vim instance exceeds MAX_HWND
     CShellExt *cs = (CShellExt*) lParam;
-    if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE;
+    if (cs->m_cntOfHWnd >= MAX_HWND)
+	return FALSE;	// stop enumeration
     // Now we get the vim window, put it into some kind of array
     cs->m_hWnd[cs->m_cntOfHWnd] = hWnd;
     cs->m_cntOfHWnd ++;
@@ -852,18 +924,18 @@ BOOL CALLBACK CShellExt::EnumWindowsProc
 
 BOOL CShellExt::LoadMenuIcon()
 {
-	char vimExeFile[BUFSIZE];
-	getGvimName(vimExeFile, 1);
-	if (vimExeFile[0] == '\0')
-		return FALSE;
-	HICON hVimIcon;
-	if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0)
-		return FALSE;
-	m_hVimIconBitmap = IconToBitmap(hVimIcon,
-		GetSysColorBrush(COLOR_MENU),
-		GetSystemMetrics(SM_CXSMICON),
-		GetSystemMetrics(SM_CYSMICON));
-	return TRUE;
+    char vimExeFile[BUFSIZE];
+    getGvimName(vimExeFile, 1);
+    if (vimExeFile[0] == '\0')
+	return FALSE;
+    HICON hVimIcon;
+    if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0)
+	return FALSE;
+    m_hVimIconBitmap = IconToBitmap(hVimIcon,
+	    GetSysColorBrush(COLOR_MENU),
+	    GetSystemMetrics(SM_CXSMICON),
+	    GetSystemMetrics(SM_CYSMICON));
+    return TRUE;
 }
 
     static char *
@@ -948,16 +1020,24 @@ STDMETHODIMP CShellExt::InvokeSingleGvim
 		FALSE,		// Set handle inheritance to FALSE.
 		0,		// No creation flags.
 		NULL,		// Use parent's environment block.
-		workingDir,  // Use parent's starting directory.
+		workingDir,	// Use parent's starting directory.
 		&si,		// Pointer to STARTUPINFO structure.
 		&pi)		// Pointer to PROCESS_INFORMATION structure.
        )
     {
-	MessageBox(
-	    hParent,
-	    _("Error creating process: Check if gvim is in your path!"),
-	    _("gvimext.dll error"),
-	    MB_OK);
+	// Use UTF-8 for gettext.
+	char *prev = set_gettext_codeset();
+
+	WCHAR *msg = W(_("Error creating process: Check if gvim is in your path!"));
+	WCHAR *title = W(_("gvimext.dll error"));
+
+	MessageBoxW(hParent, msg, title, MB_OK);
+
+	free(msg);
+	free(title);
+
+	// Restore previous codeset.
+	restore_gettext_codeset(prev);
     }
     else
     {
--- a/src/GvimExt/gvimext.h
+++ b/src/GvimExt/gvimext.h
@@ -81,21 +81,20 @@ DEFINE_GUID(CLSID_ShellExtension, 0x51ee
 class CShellExtClassFactory : public IClassFactory
 {
 protected:
-	ULONG	m_cRef;
+    ULONG	m_cRef;
 
 public:
-	CShellExtClassFactory();
-	~CShellExtClassFactory();
+    CShellExtClassFactory();
+    ~CShellExtClassFactory();
 
-	//IUnknown members
-	STDMETHODIMP			QueryInterface(REFIID, LPVOID FAR *);
-	STDMETHODIMP_(ULONG)	AddRef();
-	STDMETHODIMP_(ULONG)	Release();
+    //IUnknown members
+    STDMETHODIMP		QueryInterface(REFIID, LPVOID FAR *);
+    STDMETHODIMP_(ULONG)	AddRef();
+    STDMETHODIMP_(ULONG)	Release();
 
-	//IClassFactory members
-	STDMETHODIMP		CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
-	STDMETHODIMP		LockServer(BOOL);
-
+    //IClassFactory members
+    STDMETHODIMP		CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
+    STDMETHODIMP		LockServer(BOOL);
 };
 typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY;
 #define MAX_HWND 100
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1577,
+/**/
     1576,
 /**/
     1575,