1
0
forked from aniani/vim

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)
This commit is contained in:
K.Takata 2023-05-25 16:43:27 +01:00 committed by Bram Moolenaar
parent 097c5370ea
commit 1271572a35
5 changed files with 167 additions and 85 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
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);
hResultBmp = hMemBmp;
hMemBmp = NULL;
DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL);
SelectObject(hMemDC, hOrgBMP);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DestroyIcon(hIcon);
return hResultBmp;
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 hBackground, int width, int height)
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)(const char *) = null_libintl_gettext;
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 *
@ -262,6 +282,12 @@ null_libintl_gettext(const char *msgid)
return (char *)msgid;
}
static char *
null_libintl_textdomain(const char * /* domainname */)
{
return NULL;
}
static char *
null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */)
{
@ -269,7 +295,7 @@ null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirna
}
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(HMENU hMenu,
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(HMENU hMenu,
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(HMENU hMenu,
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(HMENU hMenu,
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(HMENU hMenu,
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,
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(UINT_PTR /* idCmd */,
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(HWND hWnd, LPARAM lParam)
// 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(HWND hWnd, LPARAM lParam)
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(HWND hWnd, LPARAM lParam)
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(HWND hParent,
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
{

View File

@ -81,21 +81,20 @@ DEFINE_GUID(CLSID_ShellExtension, 0x51eee242, 0xad87, 0x11d3, 0x9c, 0x1e, 0x0, 0
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();
//IClassFactory members
STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
STDMETHODIMP LockServer(BOOL);
//IUnknown members
STDMETHODIMP QueryInterface(REFIID, LPVOID FAR *);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
//IClassFactory members
STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR *);
STDMETHODIMP LockServer(BOOL);
};
typedef CShellExtClassFactory *LPCSHELLEXTCLASSFACTORY;
#define MAX_HWND 100

View File

@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1577,
/**/
1576,
/**/