#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?) // 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); // If we're part of a combo box, let it know we're gone #if 0 // PORTPORT: Expose fnid field in WND struct. pwndParent = REBASEPWND(pwnd, spwndParent); if (pwndParent && GETFNID(pwndParent) == FNID_COMBOBOX) { ComboBoxWndProcWorker(pwndParent, WM_PARENTNOTIFY, MAKELONG(WM_DESTROY, PTR_TO_ID(pwnd->spmenu)), (LPARAM)HWq(pwnd), FALSE); } #endif } //---------------------------------------------------------------------------// // // 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 // #if 0 // PORTPORT: How to get this info in user mode? And if we // set it to zero what is the effect? dwMaxOverlapChars = GetMaxOverlapChars(); #endif 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; }