|
|
#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "edit.h"
//---------------------------------------------------------------------------//
#define WS_EX_EDGEMASK (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)
#define GetCharABCWidthsAorW ((ped)->fAnsi ? GetCharABCWidthsA : GetCharABCWidthsW)
#define GetCharWidthAorW ((ped)->fAnsi ? GetCharWidthA : GetCharWidthW)
//---------------------------------------------------------------------------//
INT Edit_GetStateId(PED ped) { INT iStateId;
if (ped->fDisabled) { iStateId = ETS_DISABLED; } else if (ped->fReadOnly) { iStateId = ETS_READONLY; } else if (ped->fFocus) { iStateId = ETS_FOCUSED; } else if (ped->fHot) { iStateId = ETS_HOT; } else { iStateId = ETS_NORMAL; }
return iStateId; }
//---------------------------------------------------------------------------//
VOID Edit_SetMargin(PED ped, UINT wFlags, long lMarginValues, BOOL fRedraw) { BOOL fUseFontInfo = FALSE; UINT wValue, wOldLeftMargin, wOldRightMargin;
if (wFlags & EC_LEFTMARGIN) { //
// Set the left margin
//
if ((int) (wValue = (int)(short)LOWORD(lMarginValues)) < 0) { fUseFontInfo = TRUE; wValue = min((ped->aveCharWidth / 2), (int)ped->wMaxNegA); }
ped->rcFmt.left += wValue - ped->wLeftMargin; wOldLeftMargin = ped->wLeftMargin; ped->wLeftMargin = wValue; }
if (wFlags & EC_RIGHTMARGIN) { //
// Set the Right margin
//
if ((int) (wValue = (int)(short)HIWORD(lMarginValues)) < 0) { fUseFontInfo = TRUE; wValue = min((ped->aveCharWidth / 2), (int)ped->wMaxNegC); }
ped->rcFmt.right -= wValue - ped->wRightMargin; wOldRightMargin = ped->wRightMargin; ped->wRightMargin = wValue; }
if (fUseFontInfo) { if (ped->rcFmt.right - ped->rcFmt.left < 2 * ped->aveCharWidth) { TraceMsg(TF_STANDARD, "EDIT: Edit_SetMargin: rcFmt is too narrow for EC_USEFONTINFO");
if (wFlags & EC_LEFTMARGIN) { //
// Reset the left margin
//
ped->rcFmt.left += wOldLeftMargin - ped->wLeftMargin; ped->wLeftMargin = wOldLeftMargin; }
if (wFlags & EC_RIGHTMARGIN) { //
// Reset the Right margin
//
ped->rcFmt.right -= wOldRightMargin - ped->wRightMargin; ped->wRightMargin = wOldRightMargin; }
return; } }
if (fRedraw) { Edit_InvalidateClient(ped, TRUE); } }
//---------------------------------------------------------------------------//
VOID Edit_CalcMarginForDBCSFont(PED ped, BOOL fRedraw) { if (ped->fTrueType) { if (!ped->fSingle) { //
// wMaxNegA came from ABC CharWidth.
//
if (ped->wMaxNegA != 0) { Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO),fRedraw); } } else { int iMaxNegA = 0, iMaxNegC = 0; int i; PVOID lpBuffer; LPABC lpABCBuff; ABC ABCInfo; HFONT hOldFont; HDC hdc = GetDC(ped->hwnd);
if (!ped->hFont || !(hOldFont = SelectFont(hdc, ped->hFont))) { ReleaseDC(ped->hwnd, hdc); return; }
if (lpBuffer = UserLocalAlloc(0,sizeof(ABC) * 256)) { lpABCBuff = lpBuffer; GetCharABCWidthsAorW(hdc, 0, 255, lpABCBuff); } else { lpABCBuff = &ABCInfo; GetCharABCWidthsAorW(hdc, 0, 0, lpABCBuff); }
i = 0; while (TRUE) { iMaxNegA = min(iMaxNegA, lpABCBuff->abcA); iMaxNegC = min(iMaxNegC, lpABCBuff->abcC);
if (++i == 256) { break; }
if (lpBuffer) { lpABCBuff++; } else { GetCharABCWidthsAorW(hdc, i, i, lpABCBuff); } }
SelectFont(hdc, hOldFont);
if (lpBuffer) { UserLocalFree(lpBuffer); }
ReleaseDC(ped->hwnd, hdc);
if ((iMaxNegA != 0) || (iMaxNegC != 0)) { Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG((UINT)(-iMaxNegC), (UINT)(-iMaxNegA)),fRedraw); } }
} }
//---------------------------------------------------------------------------//
//
// GetCharDimensionsEx(HDC hDC, HFONT hfont, LPTEXTMETRIC lptm, LPINT lpcy)
//
// if an app set a font for vertical writing, even though we don't
// handle it with EC, the escapement of tm can be NON 0. Then cxWidth from
// GetCharDimenstions() could be 0 in GetCharDimensions().
// This will break our caller who don't expect 0 at return. So I created
// this entry for the case the caller set vertical font.
//
//
// PORTPORT: Duplicates functionality of GetCharDimensions() in prsht.c
int UserGetCharDimensionsEx(HDC hDC, HFONT hfont, LPTEXTMETRICW lptm, LPINT lpcy) { static CONST WCHAR AveCharWidthData[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; int cxWidth; TEXTMETRICW tm; LOGFONTW lf; WCHAR wchFaceName[LF_FACESIZE];
//
// Is this font vertical font ??
//
wchFaceName[0] = 0; GetTextFaceW(hDC, LF_FACESIZE, wchFaceName); if (wchFaceName[0] != L'@') { //
// if not call GDI...
//
return(GdiGetCharDimensions(hDC, lptm, lpcy)); }
if (!lptm) { lptm = &tm; }
GetTextMetricsW(hDC, lptm);
// TMPF_FIXED_PITCH
//
// If this bit is set the font is a variable pitch font.
// If this bit is clear the font is a fixed pitch font.
// Note very carefully that those meanings are the opposite of what the constant name implies.
//
if (!(lptm->tmPitchAndFamily & TMPF_FIXED_PITCH)) { //
// This is fixed pitch font....
//
cxWidth = lptm->tmAveCharWidth; } else { //
// This is variable pitch font...
//
if (hfont && GetObjectW(hfont, sizeof(LOGFONTW), &lf) && (lf.lfEscapement != 0)) { cxWidth = lptm->tmAveCharWidth; } else { SIZE size; GetTextExtentPointW(hDC, AveCharWidthData, 52, &size); cxWidth = ((size.cx / 26) + 1) / 2; } }
if (lpcy) { *lpcy = lptm->tmHeight; }
return cxWidth; }
//---------------------------------------------------------------------------//
//
// Edit_GetTextHandler AorW
//
// Copies at most maxCchToCopy chars to the buffer lpBuffer. Returns
// how many chars were actually copied. Null terminates the string based
// on the fNullTerminate flag:
// fNullTerminate --> at most (maxCchToCopy - 1) characters will be copied
// !fNullTerminate --> at most (maxCchToCopy) characters will be copied
//
ICH Edit_GetTextHandler(PED ped, ICH maxCchToCopy, LPSTR lpBuffer, BOOL fNullTerminate) { PSTR pText;
if (maxCchToCopy) { //
// Zero terminator takes the extra byte
//
if (fNullTerminate) { maxCchToCopy--; } maxCchToCopy = min(maxCchToCopy, ped->cch);
//
// Zero terminate the string
//
if (ped->fAnsi) { *(LPSTR)(lpBuffer + maxCchToCopy) = 0; } else { *(((LPWSTR)lpBuffer) + maxCchToCopy) = 0; }
pText = Edit_Lock(ped); RtlCopyMemory(lpBuffer, pText, maxCchToCopy * ped->cbChar); Edit_Unlock(ped); }
return maxCchToCopy; }
//---------------------------------------------------------------------------//
BOOL Edit_NcCreate( PED ped, HWND hwnd, LPCREATESTRUCT lpCreateStruct) { BOOL fAnsi; ULONG ulStyle; ULONG ulStyleEx;
//
// Initialize the ped
//
ped->hwnd = hwnd; ped->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);
ulStyle = GET_STYLE(ped); ulStyleEx = GET_EXSTYLE(ped);
//
// (phellyar) All strings sent to us via standard WM_* messages or
// control specific EM_* messages are expanded to unicode
// for us by user. Therefore we need worry about
// whether be are created by and ANSI app.
//
//fAnsi = TESTFLAG(GET_STATE(ped), WS_ST_ANSICREATOR);
fAnsi = 0;
ped->fEncoded = FALSE; ped->iLockLevel = 0;
ped->chLines = NULL; ped->pTabStops = NULL; ped->charWidthBuffer = NULL; ped->fAnsi = fAnsi ? 1 : 0; // Force TRUE to be 1 because its a 1 bit field
ped->cbChar = (WORD)(fAnsi ? sizeof(CHAR) : sizeof(WCHAR)); ped->hInstance = lpCreateStruct->hInstance; // IME
ped->hImcPrev = NULL_HIMC;
{ DWORD dwVer = UserGetVersion();
ped->fWin31Compat = Is310Compat(dwVer); ped->f40Compat = Is400Compat(dwVer);
}
//
// NOTE:
// The order of the following two checks is important. People can
// create edit fields with a 3D and a normal border, and we don't
// want to disallow that. But we need to detect the "no 3D border"
// border case too.
//
if ( ulStyleEx & WS_EX_EDGEMASK ) { ped->fBorder = TRUE; } else if ( ulStyle & WS_BORDER ) { ClearWindowState(hwnd, WS_BORDER); ped->fFlatBorder = TRUE; ped->fBorder = TRUE; }
if ( !(ulStyle & ES_MULTILINE) ) { ped->fSingle = TRUE; }
if ( ulStyle & WS_DISABLED ) { ped->fDisabled = TRUE; }
if ( ulStyle & ES_READONLY) { if (!ped->fWin31Compat) { //
// BACKWARD COMPATIBILITY HACK
//
// "MileStone" unknowingly sets the ES_READONLY style. So, we strip this
// style here for all Win3.0 apps (this style is new for Win3.1).
// Fix for Bug #12982 -- SANKAR -- 01/24/92 --
//
ClearWindowState(hwnd, ES_READONLY); } else { ped->fReadOnly = TRUE; } }
//
// Allocate storage for the text for the edit controls. Storage for single
// line edit controls will always get allocated in the local data segment.
// Multiline will allocate in the local ds but the app may free this and
// allocate storage elsewhere...
//
ped->hText = LocalAlloc(LHND, CCHALLOCEXTRA*ped->cbChar); if (!ped->hText) { return FALSE; }
ped->cchAlloc = CCHALLOCEXTRA; ped->lineHeight = 1;
ped->hwndParent = lpCreateStruct->hwndParent; ped->hTheme = OpenThemeData(ped->hwnd, L"Edit");
ped->wImeStatus = 0;
return (BOOL)DefWindowProc(hwnd, WM_NCCREATE, 0, (LPARAM)lpCreateStruct); }
//---------------------------------------------------------------------------//
BOOL Edit_Create(PED ped, LONG windowStyle) { HDC hdc;
//
// Get values from the window instance data structure and put
// them in the ped so that we can access them easier.
//
if ( windowStyle & ES_AUTOHSCROLL ) { ped->fAutoHScroll = 1; }
if ( windowStyle & ES_NOHIDESEL ) { ped->fNoHideSel = 1; }
ped->format = (LOWORD(windowStyle) & LOWORD(ES_FMTMASK)); if ((windowStyle & ES_RIGHT) && !ped->format) { ped->format = ES_RIGHT; }
//
// Max # chars we will initially allow
//
ped->cchTextMax = MAXTEXT;
//
// Set up undo initial conditions... (ie. nothing to undo)
//
ped->ichDeleted = (ICH)-1; ped->ichInsStart = (ICH)-1; ped->ichInsEnd = (ICH)-1;
//
// initial charset value - need to do this BEFORE EditML_Create is called
// so that we know not to fool with scrollbars if nessacary
//
hdc = Edit_GetDC(ped, TRUE); ped->charSet = (BYTE)GetTextCharset(hdc); Edit_ReleaseDC(ped, hdc, TRUE);
//
// FE_IME
// EC_INSERT_COMPOSITION_CHARACTER: Edit_Create() - call Edit_InitInsert()
//
Edit_InitInsert(ped, GetKeyboardLayout(0));
if( g_pLpkEditCallout ) { ped->pLpkEditCallout = g_pLpkEditCallout; } else { ped->pLpkEditCallout = NULL; }
return ped->pLpkEditCallout ? ped->pLpkEditCallout->EditCreate((PED0)ped, ped->hwnd) : TRUE; }
//---------------------------------------------------------------------------//
//
// Do this once at process startup. The edit control has special
// callouts in lpk.dll to help it render complex script languages
// such as Arabic. The registry probing alg executed here is the same
// as the one performed in win32k!InitializeGre.
//
// We then call GetModuleHandle rather than LoadLibrary, since lpk.dll
// will be guaranteed to be loaded and initialized already by gdi32. This
// fixes the scenario in which the user turns on complex scripts but
// doesn't reboot, which caused us to try and load lpk without it having
// been initialized on the kernel side.
//
VOID InitEditLpk() { LONG lError; HKEY hKey;
lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\LanguagePack"), 0, KEY_QUERY_VALUE, &hKey);
if (lError == ERROR_SUCCESS) { HANDLE hLpk; DWORD dwLpkShapingDlls; DWORD dwIndex; TCHAR szTemp[256]; DWORD dwTempSize; DWORD dwValueType; DWORD dwValue; DWORD dwValueSize;
dwLpkShapingDlls = 0; dwIndex = 0; do { dwTempSize = ARRAYSIZE(szTemp); dwValueSize = SIZEOF(DWORD); lError = RegEnumValue(hKey, dwIndex++, szTemp, &dwTempSize, NULL, &dwValueType, (LPVOID)&dwValue, &dwValueSize);
if ((lError == ERROR_SUCCESS) && (dwValueType == REG_DWORD)) { dwLpkShapingDlls |= 1 << dwValue; } } while (lError != ERROR_NO_MORE_ITEMS);
if (dwLpkShapingDlls != 0) { hLpk = GetModuleHandle(TEXT("LPK")); if (hLpk != NULL) { g_pLpkEditCallout = (PLPKEDITCALLOUT)GetProcAddress(hLpk, "LpkEditControl");
if (g_pLpkEditCallout == NULL) { FreeLibrary(hLpk); } } }
RegCloseKey(hKey); } }
//---------------------------------------------------------------------------//
//
// Destroys the edit control ped by freeing up all memory used by it.
//
VOID Edit_NcDestroyHandler(HWND hwnd, PED ped) { //
// ped could be NULL if WM_NCCREATE failed to create it...
//
if (ped) { //
// Free the text buffer (always present?)
//
if (TESTFLAG(GET_STYLE(ped), ES_PASSWORD)) { LPWSTR pszText = LocalLock(ped->hText); RtlSecureZeroMemory(pszText, LocalSize(ped->hText)); LocalUnlock(ped->hText); } LocalFree(ped->hText);
//
// Free up undo buffer and line start array (if present)
//
if (ped->hDeletedText != NULL) { GlobalFree(ped->hDeletedText); }
//
// Free tab stop buffer (if present)
//
if (ped->pTabStops) { UserLocalFree(ped->pTabStops); }
//
// Free line start array (if present)
//
if (ped->chLines) { UserLocalFree(ped->chLines); }
//
// Free the character width buffer (if present)
//
if (ped->charWidthBuffer) { UserLocalFree(ped->charWidthBuffer); }
//
// Free the cursor bitmap
//
if (ped->pLpkEditCallout && ped->hCaretBitmap) { DeleteObject(ped->hCaretBitmap); }
//
// Free the cached font handle
//
if ( ped->hFontSave ) { DeleteObject(ped->hFontSave); }
//
// Close an open theme handle
//
if ( ped->hTheme ) { CloseThemeData(ped->hTheme); }
//
// Free the memory used by CueBannerText
//
Str_SetPtr(&(ped->pszCueBannerText), NULL);
//
// free the allocated password font
//
if ( ped->hFontPassword ) { DeleteObject(ped->hFontPassword); }
//
// Last but not least, free the ped
//
UserLocalFree(ped); }
TraceMsg(TF_STANDARD, "EDIT: Clearing edit instance pointer."); Edit_SetPtr(hwnd, NULL); }
//---------------------------------------------------------------------------//
//
// Edit_SetPasswordCharHandler AorW
//
// Sets the password char to display.
//
VOID Edit_SetPasswordCharHandler(PED ped, UINT pwchar) { HDC hdc; SIZE size = {0};
ped->charPasswordChar = pwchar;
if (pwchar) { hdc = Edit_GetDC(ped, TRUE);
if (ped->fAnsi) { GetTextExtentPointA(hdc, (LPSTR)&pwchar, 1, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)&pwchar, 1, &size); }
GetTextExtentPointW(hdc, (LPWSTR)&pwchar, 1, &size); ped->cPasswordCharWidth = max(size.cx, 1); Edit_ReleaseDC(ped, hdc, TRUE); }
if (pwchar) { SetWindowState(ped->hwnd, ES_PASSWORD); } else { ClearWindowState(ped->hwnd, ES_PASSWORD); }
if ( g_fIMMEnabled ) { Edit_EnableDisableIME(ped); } }
//---------------------------------------------------------------------------//
//
// GetNegABCwidthInfo()
//
// This function fills up the ped->charWidthBuffer buffer with the
// negative A,B and C widths for all the characters below 0x7f in the
// currently selected font.
//
// Returns:
// TRUE, if the function succeeded.
// FALSE, if GDI calls to get the char widths have failed.
//
// Note: not used if LPK installed
//
BOOL GetNegABCwidthInfo(PED ped, HDC hdc) { LPABC lpABCbuff; int i; int CharWidthBuff[CHAR_WIDTH_BUFFER_LENGTH]; // Local char width buffer.
int iOverhang;
if (!GetCharABCWidthsA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, (LPABC)ped->charWidthBuffer)) { TraceMsg(TF_STANDARD, "UxEdit: GetNegABCwidthInfo: GetCharABCWidthsA Failed"); return FALSE; }
//
// The (A+B+C) returned for some fonts (eg: Lucida Caligraphy) does not
// equal the actual advanced width returned by GetCharWidths() minus overhang.
// This is due to font bugs. So, we adjust the 'B' width so that this
// discrepancy is removed.
// Fix for Bug #2932 --sankar-- 02/17/93
//
iOverhang = ped->charOverhang; GetCharWidthA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, (LPINT)CharWidthBuff); lpABCbuff = (LPABC)ped->charWidthBuffer; for(i = 0; i < CHAR_WIDTH_BUFFER_LENGTH; i++) { lpABCbuff->abcB = CharWidthBuff[i] - iOverhang - lpABCbuff->abcA - lpABCbuff->abcC; lpABCbuff++; }
return TRUE; }
//---------------------------------------------------------------------------//
//
// Edit_Size() -
//
// Handle sizing for an edit control's client rectangle.
// Use lprc as the bounding rectangle if specified; otherwise use the current
// client rectangle.
//
VOID Edit_Size(PED ped, LPRECT lprc, BOOL fRedraw) { RECT rc;
//
// BiDi VB32 Creates an Edit Control and immediately sends a WM_SIZE
// message which causes EXSize to be called before Edit_SetFont, which
// in turn causes a divide by zero exception below. This check for
// ped->lineHeight will pick it up safely. [samera] 3/5/97
//
if(ped->lineHeight == 0) { return; }
//
// assume that we won't be able to display the caret
//
ped->fCaretHidden = TRUE;
if ( lprc ) { CopyRect(&rc, lprc); } else { GetClientRect(ped->hwnd, &rc); }
if (!(rc.right - rc.left) || !(rc.bottom - rc.top)) { if (ped->rcFmt.right - ped->rcFmt.left) { return; }
rc.left = 0; rc.top = 0; rc.right = ped->aveCharWidth * 10; rc.bottom = ped->lineHeight; }
if (!lprc) { //
// subtract the margins from the given rectangle --
// make sure that this rectangle is big enough to have these margins.
//
if ((rc.right - rc.left) > (int)(ped->wLeftMargin + ped->wRightMargin)) { rc.left += ped->wLeftMargin; rc.right -= ped->wRightMargin; } }
//
// Leave space so text doesn't touch borders.
// For 3.1 compatibility, don't subtract out vertical borders unless
// there is room.
//
if (ped->fBorder) { INT cxBorder = GetSystemMetrics(SM_CXBORDER); INT cyBorder = GetSystemMetrics(SM_CYBORDER);
if (ped->fFlatBorder) { cxBorder *= 2; cyBorder *= 2; }
if (rc.bottom < rc.top + ped->lineHeight + 2*cyBorder) { cyBorder = 0; }
InflateRect(&rc, -cxBorder, -cyBorder); }
//
// Is the resulting rectangle too small? Don't change it then.
//
if ((!ped->fSingle) && ((rc.right - rc.left < (int) ped->aveCharWidth) || ((rc.bottom - rc.top) / ped->lineHeight == 0))) { return; }
//
// now, we know we're safe to display the caret
//
ped->fCaretHidden = FALSE;
CopyRect(&ped->rcFmt, &rc);
if (ped->fSingle) { ped->rcFmt.bottom = min(rc.bottom, rc.top + ped->lineHeight); } else { EditML_Size(ped, fRedraw); }
if (fRedraw) { InvalidateRect(ped->hwnd, NULL, TRUE); }
//
// FE_IME
// Edit_Size() - call Edit_ImmSetCompositionWindow()
//
// normally this isn't needed because WM_SIZE will cause
// WM_PAINT and the paint handler will take care of IME
// composition window. However when the edit window is
// restored from maximized window and client area is out
// of screen, the window will not be redrawn.
//
if (ped->fFocus && g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0))) { POINT pt;
GetCaretPos(&pt); Edit_ImmSetCompositionWindow(ped, pt.x, pt.y); } }
//---------------------------------------------------------------------------//
//
// Edit_SetFont AorW
//
// Sets the font used in the edit control. Warning: Memory compaction may
// occur if the font wasn't previously loaded. If the font handle passed
// in is NULL, assume the system font.
//
BOOL Edit_SetFont(PED ped, HFONT hfont, BOOL fRedraw) { TEXTMETRICW TextMetrics = {0}; HDC hdc; HFONT hOldFont=NULL; DWORD dwMaxOverlapChars; CHWIDTHINFO cwi; UINT uExtracharPos; BOOL fRet = FALSE;
hdc = GetDC(ped->hwnd); if (hdc) {
#ifdef _USE_DRAW_THEME_TEXT_
if (hfont) { ped->hFontSave = hfont; }
if ( ped->hTheme ) { //
// use the theme font if we're themed
//
HRESULT hr; LOGFONT lf; hr = GetThemeFont(ped->hTheme, hdc, EP_EDITTEXT, 0, TMT_FONT, &lf); if ( SUCCEEDED(hr) ) { hfont = CreateFontIndirect(&lf); } } #endif // _USE_DRAW_THEME_TEXT_
ped->hFont = hfont; if (ped->hFont) { //
// Since the default font is the system font, no need to select it in
// if that's what the user wants.
//
hOldFont = SelectObject(hdc, hfont); if (!hOldFont) { hfont = ped->hFont = NULL; }
//
// Get the metrics and ave char width for the currently selected font
//
//
// Call Vertical font-aware AveWidth compute function...
//
// FE_SB
ped->aveCharWidth = UserGetCharDimensionsEx(hdc, hfont, &TextMetrics, &ped->lineHeight);
//
// This might fail when people uses network fonts (or bad fonts).
//
if (ped->aveCharWidth == 0) { TraceMsg(TF_STANDARD, "EDIT: Edit_SetFont: GdiGetCharDimensions failed"); if (hOldFont != NULL) { SelectObject(hdc, hOldFont); }
//
// We've messed up the ped so let's reset the font.
// Note that we won't recurse more than once because we'll
// pass hfont == NULL.
// Too bad WM_SETFONT doesn't return a value.
//
return Edit_SetFont(ped, NULL, fRedraw); } } else { ped->aveCharWidth = UserGetCharDimensionsEx(hdc, hfont, &TextMetrics, &ped->lineHeight);
// We should always be able to get the dimensions of the system font. Just in case
// set these guys to known system font constants
if ( ped->aveCharWidth == 0 ) { ped->aveCharWidth = SYSFONT_CXCHAR; ped->lineHeight = SYSFONT_CYCHAR; } }
ped->charOverhang = TextMetrics.tmOverhang;
//
// assume that they don't have any negative widths at all.
//
ped->wMaxNegA = ped->wMaxNegC = ped->wMaxNegAcharPos = ped->wMaxNegCcharPos = 0;
//
// Check if Proportional Width Font
//
// NOTE: as SDK doc says about TEXTMETRIC:
// TMPF_FIXED_PITCH
// If this bit is set the font is a variable pitch font. If this bit is clear
// the font is a fixed pitch font. Note very carefully that those meanings are
// the opposite of what the constant name implies.
//
// Thus we have to reverse the value using logical not (fNonPropFont has 1 bit width)
//
ped->fNonPropFont = !(TextMetrics.tmPitchAndFamily & FIXED_PITCH);
//
// Check for a TrueType font
// Older app OZWIN chokes if we allocate a bigger buffer for TrueType fonts
// So, for apps older than 4.0, no special treatment for TrueType fonts.
//
if (ped->f40Compat && (TextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) { ped->fTrueType = GetCharWidthInfo(hdc, &cwi); #if DBG
if (!ped->fTrueType) { TraceMsg(TF_STANDARD, "EDIT: Edit_SetFont: GetCharWidthInfo Failed"); } #endif
} else { ped->fTrueType = FALSE; }
// FE_SB
//
// In DBCS Windows, Edit Control must handle Double Byte Character
// if tmCharSet field of textmetrics is double byte character set
// such as SHIFTJIS_CHARSET(128:Japan), HANGEUL_CHARSET(129:Korea).
//
// We call Edit_GetDBCSVector even when fAnsi is false so that we could
// treat ped->fAnsi and ped->fDBCS indivisually. I changed Edit_GetDBCSVector
// function so that it returns 0 or 1, because I would like to set ped->fDBCS
// bit field here.
//
ped->fDBCS = Edit_GetDBCSVector(ped,hdc,TextMetrics.tmCharSet); ped->charSet = TextMetrics.tmCharSet;
if (ped->fDBCS) { //
// Free the character width buffer if ped->fDBCS.
//
// I expect single GetTextExtentPoint call is faster than multiple
// GetTextExtentPoint call (because the graphic engine has a cache buffer).
// See editec.c/ECTabTheTextOut().
//
if (ped->charWidthBuffer) { LocalFree(ped->charWidthBuffer); ped->charWidthBuffer = NULL; }
//
// if FullWidthChar : HalfWidthChar == 2 : 1....
//
// TextMetrics.tmMaxCharWidth = FullWidthChar width
// ped->aveCharWidth = HalfWidthChar width
//
if (ped->fNonPropFont && ((ped->aveCharWidth * 2) == TextMetrics.tmMaxCharWidth)) { ped->fNonPropDBCS = TRUE; } else { ped->fNonPropDBCS = FALSE; }
} else { //
// Since the font has changed, let us obtain and save the character width
// info for this font.
//
// First left us find out if the maximum chars that can overlap due to
// negative widths. Since we can't access USER globals, we make a call here.
//
if (!(ped->fSingle || ped->pLpkEditCallout)) { //
// Is this a multiline edit control with no LPK present?
//
UINT wBuffSize; LPINT lpCharWidthBuff; SHORT i;
//
// For multiline edit controls, we maintain a buffer that contains
// the character width information.
//
wBuffSize = (ped->fTrueType) ? (CHAR_WIDTH_BUFFER_LENGTH * sizeof(ABC)) : (CHAR_WIDTH_BUFFER_LENGTH * sizeof(int));
if (ped->charWidthBuffer) { //
// If buffer already present
//
lpCharWidthBuff = ped->charWidthBuffer; ped->charWidthBuffer = UserLocalReAlloc(lpCharWidthBuff, wBuffSize, HEAP_ZERO_MEMORY); if (ped->charWidthBuffer == NULL) { UserLocalFree((HANDLE)lpCharWidthBuff); } } else { ped->charWidthBuffer = UserLocalAlloc(HEAP_ZERO_MEMORY, wBuffSize); }
if (ped->charWidthBuffer != NULL) { if (ped->fTrueType) { ped->fTrueType = GetNegABCwidthInfo(ped, hdc); }
//
// It is possible that the above attempts could have failed and reset
// the value of fTrueType. So, let us check that value again.
//
if (!ped->fTrueType) { if (!GetCharWidthA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, ped->charWidthBuffer)) { UserLocalFree((HANDLE)ped->charWidthBuffer); ped->charWidthBuffer=NULL; } else { //
// We need to subtract out the overhang associated with
// each character since GetCharWidth includes it...
//
for (i=0;i < CHAR_WIDTH_BUFFER_LENGTH;i++) { ped->charWidthBuffer[i] -= ped->charOverhang; } } } } } }
{ //
// Calculate MaxNeg A C metrics
//
dwMaxOverlapChars = 0; if (ped->fTrueType) { if (cwi.lMaxNegA < 0) { ped->wMaxNegA = -cwi.lMaxNegA; } else { ped->wMaxNegA = 0; }
if (cwi.lMaxNegC < 0) { ped->wMaxNegC = -cwi.lMaxNegC; } else { ped->wMaxNegC = 0; }
if (cwi.lMinWidthD != 0) { ped->wMaxNegAcharPos = (ped->wMaxNegA + cwi.lMinWidthD - 1) / cwi.lMinWidthD; ped->wMaxNegCcharPos = (ped->wMaxNegC + cwi.lMinWidthD - 1) / cwi.lMinWidthD; if (ped->wMaxNegA + ped->wMaxNegC > (UINT)cwi.lMinWidthD) { uExtracharPos = (ped->wMaxNegA + ped->wMaxNegC - 1) / cwi.lMinWidthD; ped->wMaxNegAcharPos += uExtracharPos; ped->wMaxNegCcharPos += uExtracharPos; } } else { ped->wMaxNegAcharPos = LOWORD(dwMaxOverlapChars); // Left
ped->wMaxNegCcharPos = HIWORD(dwMaxOverlapChars); // Right
}
} else if (ped->charOverhang != 0) { //
// Some bitmaps fonts (i.e., italic) have under/overhangs;
// this is pretty much like having negative A and C widths.
//
ped->wMaxNegA = ped->wMaxNegC = ped->charOverhang; ped->wMaxNegAcharPos = LOWORD(dwMaxOverlapChars); // Left
ped->wMaxNegCcharPos = HIWORD(dwMaxOverlapChars); // Right
} }
if (!hfont) { //
// We are getting the stats for the system font so update the system
// font fields in the ed structure since we use these when calculating
// some spacing.
//
ped->cxSysCharWidth = ped->aveCharWidth; ped->cySysCharHeight= ped->lineHeight; } else if (hOldFont) { SelectObject(hdc, hOldFont); }
if (ped->fFocus) { UINT cxCaret;
SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0);
//
// Update the caret.
//
HideCaret(ped->hwnd); DestroyCaret();
if (ped->pLpkEditCallout) { ped->pLpkEditCallout->EditCreateCaret((PED0)ped, hdc, cxCaret, ped->lineHeight, 0); } else { CreateCaret(ped->hwnd, (HBITMAP)NULL, cxCaret, ped->lineHeight); } ShowCaret(ped->hwnd); }
ReleaseDC(ped->hwnd, hdc);
//
// Update password character.
//
if (ped->charPasswordChar) { Edit_SetPasswordCharHandler(ped, ped->charPasswordChar); }
//
// If it is a TrueType font and it's a new app, set both the margins at the
// max negative width values for all types of the edit controls.
// (NOTE: Can't use ped->f40Compat here because edit-controls inside dialog
// boxes without DS_LOCALEDIT style are always marked as 4.0 compat.
// This is the fix for NETBENCH 3.0)
//
if (ped->fTrueType && ped->f40Compat) { if (ped->fDBCS) { //
// For DBCS TrueType Font, we calc margin from ABC width.
//
Edit_CalcMarginForDBCSFont(ped, fRedraw); } else { Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO), fRedraw); } }
//
// We need to calc maxPixelWidth when font changes.
// If the word-wrap is ON, then this is done in EditML_Size() called later.
//
if((!ped->fSingle) && (!ped->fWrap)) { EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL); }
//
// Recalc the layout.
//
Edit_Size(ped, NULL, fRedraw);
if ( ped->fFocus && ImmIsIME(GetKeyboardLayout(0)) ) { Edit_SetCompositionFont( ped ); }
fRet = TRUE; }
return fRet; }
//---------------------------------------------------------------------------//
//
// Edit_IsCharNumeric AorW
//
// Tests whether the character entered is a numeral.
// For multiline and singleline edit controls with the ES_NUMBER style.
//
BOOL Edit_IsCharNumeric(PED ped, DWORD keyPress) { WORD wCharType;
if (ped->fAnsi) { char ch = (char)keyPress; LCID lcid = (LCID)((ULONG_PTR)GetKeyboardLayout(0) & 0xFFFF); GetStringTypeA(lcid, CT_CTYPE1, &ch, 1, &wCharType); } else { WCHAR wch = (WCHAR)keyPress; GetStringTypeW(CT_CTYPE1, &wch, 1, &wCharType); } return (wCharType & C1_DIGIT ? TRUE : FALSE); }
//---------------------------------------------------------------------------//
VOID Edit_EnableDisableIME(PED ped) { if ( ped->fReadOnly || ped->charPasswordChar ) { //
// IME should be disabled
//
HIMC hImc; hImc = ImmGetContext( ped->hwnd );
if ( hImc != NULL_HIMC ) { ImmReleaseContext( ped->hwnd, hImc ); ped->hImcPrev = ImmAssociateContext( ped->hwnd, NULL_HIMC ); }
} else { //
// IME should be enabled
//
if ( ped->hImcPrev != NULL_HIMC ) { ped->hImcPrev = ImmAssociateContext( ped->hwnd, ped->hImcPrev );
//
// Font and the caret position might be changed while
// IME was being disabled. Set those now if the window
// has the focus.
//
if ( ped->fFocus ) { POINT pt;
Edit_SetCompositionFont( ped );
GetCaretPos( &pt ); Edit_ImmSetCompositionWindow( ped, pt.x, pt.y ); } } }
Edit_InitInsert(ped, GetKeyboardLayout(0)); }
//---------------------------------------------------------------------------//
VOID Edit_ImmSetCompositionWindow(PED ped, LONG x, LONG y) { COMPOSITIONFORM cf = {0}; COMPOSITIONFORM cft = {0}; RECT rcScreenWindow; HIMC hImc;
hImc = ImmGetContext(ped->hwnd); if ( hImc != NULL_HIMC ) { if ( ped->fFocus ) { GetWindowRect( ped->hwnd, &rcScreenWindow);
//
// assuming RECT.left is the first and and RECT.top is the second field
//
MapWindowPoints( ped->hwnd, HWND_DESKTOP, (LPPOINT)&rcScreenWindow, 2); if (ped->fInReconversion) { DWORD dwPoint = (DWORD)(ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_POSFROMCHAR, ped->ichMinSel, 0);
x = GET_X_LPARAM(dwPoint); y = GET_Y_LPARAM(dwPoint);
TraceMsg(TF_STANDARD, "UxEdit: Edit_ImmSetCompositionWindow: fInReconversion (%d,%d)", x, y); }
//
// The window currently has the focus.
//
if (ped->fSingle) { //
// Single line edit control.
//
cf.dwStyle = CFS_POINT; cf.ptCurrentPos.x = x; cf.ptCurrentPos.y = y; SetRectEmpty(&cf.rcArea);
} else { //
// Multi line edit control.
//
cf.dwStyle = CFS_RECT; cf.ptCurrentPos.x = x; cf.ptCurrentPos.y = y; cf.rcArea = ped->rcFmt; } ImmGetCompositionWindow( hImc, &cft ); if ( (!RtlEqualMemory(&cf,&cft,sizeof(COMPOSITIONFORM))) || (ped->ptScreenBounding.x != rcScreenWindow.left) || (ped->ptScreenBounding.y != rcScreenWindow.top) ) {
ped->ptScreenBounding.x = rcScreenWindow.left; ped->ptScreenBounding.y = rcScreenWindow.top; ImmSetCompositionWindow( hImc, &cf ); } } ImmReleaseContext( ped->hwnd, hImc ); } }
//---------------------------------------------------------------------------//
VOID Edit_SetCompositionFont(PED ped) { HIMC hImc; LOGFONTW lf;
hImc = ImmGetContext( ped->hwnd ); if (hImc != NULL_HIMC) { if (ped->hFont) { GetObjectW(ped->hFont, sizeof(LOGFONTW), (LPLOGFONTW)&lf); } else { GetObjectW(GetStockObject(SYSTEM_FONT), sizeof(LOGFONTW), (LPLOGFONTW)&lf); }
ImmSetCompositionFontW( hImc, &lf ); ImmReleaseContext( ped->hwnd, hImc ); } }
//---------------------------------------------------------------------------//
//
// Edit_InitInsert
//
// this function is called when:
// 1) a edit control window is initialized
// 2) active keyboard layout of current thread is changed
// 3) read only attribute of this edit control is changed
//
VOID Edit_InitInsert( PED ped, HKL hkl ) { ped->fKorea = FALSE; ped->fInsertCompChr = FALSE; ped->fNoMoveCaret = FALSE; ped->fResultProcess = FALSE;
if (g_fIMMEnabled && ImmIsIME(hkl) ) { if (PRIMARYLANGID(LOWORD(HandleToUlong(hkl))) == LANG_KOREAN ) { ped->fKorea = TRUE; }
//
// LATER:this flag should be set based on the IME caps
// retrieved from IME. (Such IME caps should be defined)
// For now, we can safely assume that only Korean IMEs
// set CS_INSERTCHAR.
//
if ( ped->fKorea ) { ped->fInsertCompChr = TRUE; } }
//
// if we had a composition character, the shape of caret
// is changed. We need to reset the caret shape.
//
if ( ped->fReplaceCompChr ) { ped->fReplaceCompChr = FALSE; Edit_SetCaretHandler( ped ); } }
//---------------------------------------------------------------------------//
VOID Edit_SetCaretHandler(PED ped) { HDC hdc; PSTR pText; SIZE size = {0};
//
// In any case destroy caret beforehand otherwise SetCaretPos()
// will get crazy.. win95d-B#992,B#2370
//
if (ped->fFocus) { HideCaret(ped->hwnd); DestroyCaret(); if ( ped->fReplaceCompChr ) {
hdc = Edit_GetDC(ped, TRUE ); pText = Edit_Lock(ped);
if ( ped->fAnsi) { GetTextExtentPointA(hdc, pText + ped->ichCaret, 2, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)pText + ped->ichCaret, 1, &size); }
Edit_Unlock(ped); Edit_ReleaseDC(ped, hdc, TRUE);
CreateCaret(ped->hwnd, (HBITMAP)NULL, size.cx, ped->lineHeight); } else { CreateCaret(ped->hwnd, (HBITMAP)NULL, (ped->cxSysCharWidth > ped->aveCharWidth ? 1 : 2), ped->lineHeight); }
hdc = Edit_GetDC(ped, TRUE ); if ( ped->fSingle ) { EditSL_SetCaretPosition( ped, hdc ); } else { EditML_SetCaretPosition( ped, hdc ); }
Edit_ReleaseDC(ped, hdc, TRUE); ShowCaret(ped->hwnd); } }
//---------------------------------------------------------------------------//
#define GET_COMPOSITION_STRING (ped->fAnsi ? ImmGetCompositionStringA : ImmGetCompositionStringW)
BOOL Edit_ResultStrHandler(PED ped) { HIMC himc; LPSTR lpStr; LONG dwLen;
ped->fInsertCompChr = FALSE; // clear the state
ped->fNoMoveCaret = FALSE;
himc = ImmGetContext(ped->hwnd); if ( himc == NULL_HIMC ) { return FALSE; }
dwLen = GET_COMPOSITION_STRING(himc, GCS_RESULTSTR, NULL, 0);
if (dwLen == 0) { ImmReleaseContext(ped->hwnd, himc); return FALSE; }
dwLen *= ped->cbChar; dwLen += ped->cbChar;
lpStr = (LPSTR)GlobalAlloc(GPTR, dwLen); if (lpStr == NULL) { ImmReleaseContext(ped->hwnd, himc); return FALSE; }
GET_COMPOSITION_STRING(himc, GCS_RESULTSTR, lpStr, dwLen);
if (ped->fSingle) { EditSL_ReplaceSel(ped, lpStr); } else { EditML_ReplaceSel(ped, lpStr); }
GlobalFree((HGLOBAL)lpStr);
ImmReleaseContext(ped->hwnd, himc);
ped->fReplaceCompChr = FALSE; ped->fNoMoveCaret = FALSE; ped->fResultProcess = FALSE;
Edit_SetCaretHandler(ped);
return TRUE; }
//---------------------------------------------------------------------------//
LRESULT Edit_ImeComposition(PED ped, WPARAM wParam, LPARAM lParam) { INT ich; LRESULT lReturn = 1; HDC hdc; BOOL fSLTextUpdated = FALSE; ICH iResult; HIMC hImc; BYTE TextBuf[4];
if (!ped->fInsertCompChr) { if (lParam & GCS_RESULTSTR) { Edit_InOutReconversionMode(ped, FALSE);
if (!ped->fKorea && ped->wImeStatus & EIMES_GETCOMPSTRATONCE) { ResultAtOnce: Edit_ResultStrHandler(ped); lParam &= ~GCS_RESULTSTR; } } return DefWindowProc(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam); }
//
// In case of Ansi edit control, the length of minimum composition string
// is 2. Check here maximum byte of edit control.
//
if( ped->fAnsi && ped->cchTextMax == 1 ) { HIMC hImc;
hImc = ImmGetContext( ped->hwnd ); ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L); ImmReleaseContext( ped->hwnd, hImc ); MessageBeep(MB_ICONEXCLAMATION); return lReturn; }
//
// Don't move this after CS_NOMOVECARET check.
// In case if skip the message, fNoMoveCaret should not be set.
//
if ((lParam & CS_INSERTCHAR) && ped->fResultProcess) { //
// Now we're in result processing. GCS_RESULTSTR ends up
// to WM_IME_CHAR and WM_CHAR. Since WM_CHAR is posted,
// the message(s) will come later than this CS_INSERTCHAR
// message. This composition character should be handled
// after the WM_CHAR message(s).
//
if(ped->fAnsi) { PostMessageA(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam); } else { PostMessageW(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam); }
ped->fResultProcess = FALSE;
return lReturn; }
if (lParam & GCS_RESULTSTR) { if (!ped->fKorea && ped->wImeStatus & EIMES_GETCOMPSTRATONCE) { goto ResultAtOnce; }
ped->fResultProcess = TRUE; if ( ped->fReplaceCompChr ) { //
// we have a DBCS character to be replaced.
// let's delete it before inserting the new one.
//
ich = (ped->fAnsi) ? 2 : 1; ped->fReplaceCompChr = FALSE; ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch); ped->ichMinSel = ped->ichCaret;
if ( Edit_DeleteText( ped ) > 0 ) { if ( ped->fSingle ) { //
// Update the display
//
Edit_NotifyParent(ped, EN_UPDATE); hdc = Edit_GetDC(ped,FALSE); EditSL_DrawText(ped, hdc, 0); Edit_ReleaseDC(ped,hdc,FALSE); //
// Tell parent our text contents changed.
//
Edit_NotifyParent(ped, EN_CHANGE); } } Edit_SetCaretHandler( ped ); }
} else if (lParam & CS_INSERTCHAR) {
//
// If we are in the middle of a mousedown command, don't do anything.
//
if (ped->fMouseDown) { return lReturn; }
//
// We can safely assume that interimm character is always DBCS.
//
ich = ( ped->fAnsi ) ? 2 : 1;
if ( ped->fReplaceCompChr ) { //
// we have a character to be replaced.
// let's delete it before inserting the new one.
// when we have a composition characters, the
// caret is placed before the composition character.
//
ped->ichMaxSel = min(ped->ichCaret+ich, ped->cch); ped->ichMinSel = ped->ichCaret; }
//
// let's delete current selected text or composition character
//
if ( ped->fSingle ) { if ( Edit_DeleteText( ped ) > 0 ) { fSLTextUpdated = TRUE; } } else { EditML_DeleteText( ped ); }
//
// When the composition charcter is canceled, IME may give us NULL wParam,
// with CS_INSERTCHAR flag on. We shouldn't insert a NULL character.
//
if ( wParam != 0 ) {
if ( ped->fAnsi ) { TextBuf[0] = HIBYTE(LOWORD(wParam)); // leading byte
TextBuf[1] = LOBYTE(LOWORD(wParam)); // trailing byte
TextBuf[2] = '\0'; } else { TextBuf[0] = LOBYTE(LOWORD(wParam)); TextBuf[1] = HIBYTE(LOWORD(wParam)); TextBuf[2] = '\0'; TextBuf[3] = '\0'; }
if ( ped->fSingle ) { iResult = EditSL_InsertText( ped, (LPSTR)TextBuf, ich ); if (iResult == 0) { //
// Couldn't insert the text, for e.g. the text exceeded the limit.
//
MessageBeep(0); } else if (iResult > 0) { //
// Remember we need to update the text.
//
fSLTextUpdated = TRUE; }
} else { iResult = EditML_InsertText( ped, (LPSTR)TextBuf, ich, TRUE); }
if ( iResult > 0 ) { //
// ped->fReplaceCompChr will be reset:
//
// 1) when the character is finalized.
// we will receive GCS_RESULTSTR
//
// 2) when the character is canceled.
//
// we will receive WM_IME_COMPOSITION|CS_INSERTCHAR
// with wParam == 0 (in case of user types backspace
// at the first element of composition character).
//
// or
//
// we will receive WM_IME_ENDCOMPOSITION message
//
ped->fReplaceCompChr = TRUE;
//
// Caret should be placed BEFORE the composition
// character.
//
ped->ichCaret = max( 0, (INT)ped->ichCaret - ich); Edit_SetCaretHandler( ped ); } else { //
// We failed to insert a character. We might run out
// of memory, or reached to the text size limit. let's
// cancel the composition character.
//
hImc = ImmGetContext(ped->hwnd); ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ImmReleaseContext(ped->hwnd, hImc);
ped->fReplaceCompChr = FALSE; Edit_SetCaretHandler( ped ); } } else { //
// the composition character is canceled.
//
ped->fReplaceCompChr = FALSE; Edit_SetCaretHandler( ped ); }
//
// We won't notify parent the text change
// because the composition character has
// not been finalized.
//
if ( fSLTextUpdated ) { //
// Update the display
//
Edit_NotifyParent(ped, EN_UPDATE);
hdc = Edit_GetDC(ped,FALSE);
if ( ped->fReplaceCompChr ) { //
// move back the caret to the original position
// temporarily so that our new block cursor can
// be located within the visible area of window.
//
ped->ichCaret = min( ped->cch, ped->ichCaret + ich); EditSL_ScrollText(ped, hdc); ped->ichCaret = max( 0, (INT)ped->ichCaret - ich); } else { EditSL_ScrollText(ped, hdc); } EditSL_DrawText(ped, hdc, 0);
Edit_ReleaseDC(ped,hdc,FALSE);
//
// Tell parent our text contents changed.
//
Edit_NotifyParent(ped, EN_CHANGE); } return lReturn; }
return DefWindowProc(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam); }
//---------------------------------------------------------------------------//
//
// HanjaKeyHandler
//
// VK_HANJA handler - Korean only
//
BOOL HanjaKeyHandler(PED ped) { BOOL changeSelection = FALSE;
if (ped->fKorea && !ped->fReadOnly) { ICH oldCaret = ped->ichCaret;
if (ped->fReplaceCompChr) { return FALSE; }
if (ped->ichMinSel < ped->ichMaxSel) { ped->ichCaret = ped->ichMinSel; }
if (!ped->cch || ped->cch == ped->ichCaret) { ped->ichCaret = oldCaret; MessageBeep(MB_ICONEXCLAMATION); return FALSE; }
if (ped->fAnsi) { if (ImmEscapeA(GetKeyboardLayout(0), ImmGetContext(ped->hwnd), IME_ESC_HANJA_MODE, (Edit_Lock(ped) + ped->ichCaret * ped->cbChar))) { changeSelection = TRUE; } else { ped->ichCaret = oldCaret; }
Edit_Unlock(ped); } else { if (ImmEscapeW(GetKeyboardLayout(0), ImmGetContext(ped->hwnd), IME_ESC_HANJA_MODE, (Edit_Lock(ped) + ped->ichCaret * ped->cbChar))) { changeSelection = TRUE; } else { ped->ichCaret = oldCaret; }
Edit_Unlock(ped); } }
return changeSelection; }
//---------------------------------------------------------------------------//
// Edit_RequestHandler()
//
// Handles WM_IME_REQUEST message originated by IME
//
#define MAX_ECDOCFEED 20
ICH Edit_ImeGetDocFeedMin(PED ped, LPSTR lpstr) { ICH ich;
if (ped->ichMinSel > MAX_ECDOCFEED) { ich = ped->ichMinSel - MAX_ECDOCFEED; ich = Edit_AdjustIch(ped, lpstr, ich); } else { ich = 0; }
return ich; }
ICH Edit_ImeGetDocFeedMax(PED ped, LPSTR lpstr) { ICH ich;
if ((ped->cch - ped->ichMaxSel) > MAX_ECDOCFEED) { ich = ped->ichMaxSel + MAX_ECDOCFEED; ich = Edit_AdjustIch(ped, lpstr, ich); } else { ich = ped->cch; }
return ich; }
LRESULT Edit_RequestHandler(PED ped, WPARAM dwSubMsg, LPARAM lParam) { LRESULT lreturn = 0L;
switch (dwSubMsg) { case IMR_CONFIRMRECONVERTSTRING:
//
// CHECK VERSION of the structure
//
if (lParam && ((LPRECONVERTSTRING)lParam)->dwVersion != 0) { TraceMsg(TF_STANDARD, "Edit_RequestHandler: RECONVERTSTRING dwVersion is not expected.", ((LPRECONVERTSTRING)lParam)->dwVersion); return 0L; }
if (lParam && ped && ped->fFocus && ped->hText && ImmIsIME(GetKeyboardLayout(0))) { LPVOID lpSrc; lpSrc = Edit_Lock(ped); if (lpSrc == NULL) { TraceMsg(TF_STANDARD, "Edit_RequestHandler: LOCALLOCK(ped) failed."); } else { LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)lParam; ICH ichStart; ICH ichEnd; UINT cchLen;
ichStart = Edit_ImeGetDocFeedMin(ped, lpSrc); ichEnd = Edit_ImeGetDocFeedMax(ped, lpSrc); UserAssert(ichEnd >= ichStart);
cchLen = ichEnd - ichStart; // holds character count.
Edit_Unlock(ped);
if (lpRCS->dwStrLen != cchLen) { TraceMsg(TF_STANDARD, "Edit_RequestHandler: the given string length is not expected."); } else { ICH ichSelStart; ICH ichSelEnd;
ichSelStart = ichStart + (lpRCS->dwCompStrOffset / ped->cbChar); ichSelEnd = ichSelStart + lpRCS->dwCompStrLen;
(ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd);
lreturn = 1L; } } } break;
case IMR_RECONVERTSTRING: //
// CHECK VERSION of the structure
//
if (lParam && ((LPRECONVERTSTRING)lParam)->dwVersion != 0) { TraceMsg(TF_STANDARD, "UxEdit: Edit_RequestHandler: RECONVERTSTRING dwVersion is not expected.");
return 0L; }
if (ped && ped->fFocus && ped->hText && ImmIsIME(GetKeyboardLayout(0))) { ICH ichStart; ICH ichEnd; UINT cchLen; UINT cchSelLen; LPVOID lpSrc; lpSrc = Edit_Lock(ped); if (lpSrc == NULL) { TraceMsg(TF_STANDARD, "Edit_RequestHandler: LOCALLOCK(ped) failed."); return 0L; }
ichStart = Edit_ImeGetDocFeedMin(ped, lpSrc); ichEnd = Edit_ImeGetDocFeedMax(ped, lpSrc); UserAssert(ichEnd >= ichStart);
cchLen = ichEnd - ichStart; // holds character count.
cchSelLen = ped->ichMaxSel - ped->ichMinSel; // holds character count.
if (cchLen == 0) { // if we have no selection,
// just return 0.
break; }
UserAssert(ped->cbChar == sizeof(BYTE) || ped->cbChar == sizeof(WCHAR));
// This Edit Control has selection.
if (lParam == 0) { //
// IME just want to get required size for buffer.
// cchLen + 1 is needed to reserve room for trailing L'\0'.
// ~~~~
lreturn = sizeof(RECONVERTSTRING) + (cchLen + 1) * ped->cbChar; } else { LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)lParam; LPVOID lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING);
// check buffer size
// if the given buffer is smaller than actual needed size,
// shrink our size to fit the buffer
if ((INT)lpRCS->dwSize <= sizeof(RECONVERTSTRING) + cchLen * ped->cbChar) { TraceMsg(TF_STANDARD, "UxEdit: Edit_Request: ERR09"); cchLen = (lpRCS->dwSize - sizeof(RECONVERTSTRING)) / ped->cbChar - ped->cbChar; }
lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); // buffer begins just after RECONVERTSTRING
lpRCS->dwCompStrOffset = lpRCS->dwTargetStrOffset = (ped->ichMinSel - ichStart) * ped->cbChar; // BYTE count offset
lpRCS->dwStrLen = cchLen; // TCHAR count
lpRCS->dwCompStrLen = lpRCS->dwTargetStrLen = cchSelLen; // TCHAR count
RtlCopyMemory(lpDest, (LPBYTE)lpSrc + ichStart * ped->cbChar, cchLen * ped->cbChar); // Null-Terminate the string
if (ped->fAnsi) { LPBYTE psz = (LPBYTE)lpDest; psz[cchLen] = '\0'; } else { LPWSTR pwsz = (LPWSTR)lpDest; pwsz[cchLen] = L'\0'; } Edit_Unlock(ped); // final buffer size
lreturn = sizeof(RECONVERTSTRING) + (cchLen + 1) * ped->cbChar;
Edit_InOutReconversionMode(ped, TRUE); Edit_ImmSetCompositionWindow(ped, 0, 0); }
} break; }
return lreturn; }
|