#include "ctlspriv.h" #pragma hdrstop #include "usrctl32.h" #include "edit.h" //---------------------------------------------------------------------------// // // Forwards // ICH Edit_FindTabA(LPSTR, ICH); ICH Edit_FindTabW(LPWSTR, ICH); HBRUSH Edit_GetControlBrush(PED, HDC, LONG); NTSYSAPI VOID NTAPI RtlRunEncodeUnicodeString( PUCHAR Seed OPTIONAL, PUNICODE_STRING String ); NTSYSAPI VOID NTAPI RtlRunDecodeUnicodeString( UCHAR Seed, PUNICODE_STRING String ); // // private export from GDI // UINT WINAPI QueryFontAssocStatus(void); #define umin(a, b) \ ((unsigned)(a) < (unsigned)(b) ? (unsigned)(a) : (unsigned)(b)) #define umax(a, b) \ ((unsigned)(a) > (unsigned)(b) ? (unsigned)(a) : (unsigned)(b)) #define UNICODE_CARRIAGERETURN ((WCHAR)0x0d) #define UNICODE_LINEFEED ((WCHAR)0x0a) #define UNICODE_TAB ((WCHAR)0x09) // // IME Menu IDs // #define ID_IMEOPENCLOSE 10001 #define ID_SOFTKBDOPENCLOSE 10002 #define ID_RECONVERTSTRING 10003 #define ID_EDITTIMER 10007 #define EDIT_TIPTIMEOUT 10000 #pragma code_seg(CODESEG_INIT) //---------------------------------------------------------------------------// // // InitEditClass() - Registers the control's window class // BOOL InitEditClass(HINSTANCE hInstance) { WNDCLASS wc; wc.lpfnWndProc = Edit_WndProc; wc.lpszClassName = WC_EDIT; wc.style = CS_GLOBALCLASS | CS_PARENTDC | CS_DBLCLKS; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(PED); wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_IBEAM); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS)); } #pragma code_seg() //---------------------------------------------------------------------------// // PSTR Edit_Lock(PED ped) { PSTR ptext = LocalLock(ped->hText); ped->iLockLevel++; // // If this is the first lock of the text and the text is encoded // decode the text. // //TraceMsg(TF_STANDARD, "EDIT: lock : %d '%10s'", ped->iLockLevel, ptext); if (ped->iLockLevel == 1 && ped->fEncoded) { // // rtlrundecode can't handle zero length strings // if (ped->cch != 0) { STRING string; string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar); string.Buffer = ptext; RtlRunDecodeUnicodeString(ped->seed, (PUNICODE_STRING)&string); //TraceMsg(TF_STANDARD, "EDIT: Decoding: '%10s'", ptext); } ped->fEncoded = FALSE; } return ptext; } //---------------------------------------------------------------------------// // VOID Edit_Unlock(PED ped) { // // if we are removing the last lock on the text and the password // character is set then encode the text // //TraceMsg(TF_STANDARD, "EDIT: unlock: %d '%10s'", ped->iLockLevel, ped->ptext); if (ped->charPasswordChar && ped->iLockLevel == 1 && ped->cch != 0) { UNICODE_STRING string; string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar); string.Buffer = LocalLock(ped->hText); RtlRunEncodeUnicodeString(&(ped->seed), &string); //TraceMsg(TF_STANDARD, "EDIT: Encoding: '%10s'", ped->ptext); ped->fEncoded = TRUE; LocalUnlock(ped->hText); } LocalUnlock(ped->hText); ped->iLockLevel--; } //---------------------------------------------------------------------------// // // GetActualNegA() // // For a given strip of text, this function computes the negative A width // for the whole strip and returns the value as a postive number. // It also fills the NegAInfo structure with details about the postion // of this strip that results in this Negative A. // UINT GetActualNegA(HDC hdc, PED ped, INT x, LPSTR lpstring, ICH ichString, INT nCount, LPSTRIPINFO NegAInfo) { INT iCharCount, i; INT iLeftmostPoint = x; PABC pABCwidthBuff; UINT wCharIndex; INT xStartPoint = x; ABC abc; // // To begin with, let us assume that there is no negative A width for // this strip and initialize accodingly. // NegAInfo->XStartPos = x; NegAInfo->lpString = lpstring; NegAInfo->nCount = 0; NegAInfo->ichString = ichString; // // If the current font is not a TrueType font, then there can not be any // negative A widths. // if (!ped->fTrueType) { if(!ped->charOverhang) { return 0; } else { NegAInfo->nCount = min(nCount, (INT)ped->wMaxNegAcharPos); return ped->charOverhang; } } // // How many characters are to be considered for computing Negative A ? // iCharCount = min(nCount, (INT)ped->wMaxNegAcharPos); // // Do we have the info on individual character's widths? // if(!ped->charWidthBuffer) { // // No! So, let us tell them to consider all the characters. // NegAInfo->nCount = iCharCount; return (iCharCount * ped->aveCharWidth); } pABCwidthBuff = (PABC)ped->charWidthBuffer; if (ped->fAnsi) { for (i = 0; i < iCharCount; i++) { wCharIndex = (UINT)(*((PUCHAR)lpstring)); if (*lpstring == VK_TAB) { // // To play it safe, we assume that this tab results in a tab length of // 1 pixel because this is the minimum possible tab length. // x++; } else { if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { // // Add the 'A' width. // x += pABCwidthBuff[wCharIndex].abcA; } else { GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc); x += abc.abcA; } if (x < iLeftmostPoint) { // // Reset the leftmost point. // iLeftmostPoint = x; } if (x < xStartPoint) { // // 'i' is index; To get the count add 1. // NegAInfo->nCount = i+1; } if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC; } else { x += abc.abcB + abc.abcC; } } lpstring++; } } else { LPWSTR lpwstring = (LPWSTR)lpstring; for (i = 0; i < iCharCount; i++) { wCharIndex = *lpwstring ; if (*lpwstring == VK_TAB) { // // To play it safe, we assume that this tab results in a tab length of // 1 pixel because this is the minimum possible tab length. // x++; } else { if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { // // Add the 'A' width. // x += pABCwidthBuff[wCharIndex].abcA; } else { GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc); x += abc.abcA ; } if (x < iLeftmostPoint) { // // Reset the leftmost point. // iLeftmostPoint = x; } if (x < xStartPoint) { // // 'i' is index; To get the count add 1. // NegAInfo->nCount = i+1; } if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC; } else { x += abc.abcB + abc.abcC; } } lpwstring++; } } // // Let us return the negative A for the whole strip as a positive value. // return (UINT)(xStartPoint - iLeftmostPoint); } //---------------------------------------------------------------------------// // // Edit_IsAncestorActive() // // Returns whether or not we're the child of an "active" window. Looks for // the first parent window that has a caption. // // This is a function because we might use it elsewhere when getting left // clicked on, etc. // BOOL Edit_IsAncestorActive(HWND hwnd) { BOOL fResult = TRUE; // // We want to return TRUE always for top level windows. That's because // of how WM_MOUSEACTIVATE works. If we see the click at all, the // window is active. However, if we reach a child ancestor that has // a caption, return the frame-on style bit. // // Note that calling FlashWindow() will have an effect. If the user // clicks on an edit field in a child window that is flashed off, nothing // will happen unless the window stops flashing and ncactivates first. // for(; hwnd != NULL; hwnd = GetParent(hwnd)) { PWW pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS); // // Bail out if some parent window isn't 4.0 compatible or we've // reached the top. Fixes compatibility problems with 3.x apps, // especially MFC samples. // if (!TESTFLAG(pww->dwState2, WS_S2_WIN40COMPAT) || !TESTFLAG(pww->dwStyle, WS_CHILD)) { break; } else if (TESTFLAG(pww->dwState, WS_ST_CPRESENT)) { fResult = (TESTFLAG(pww->dwState, WS_ST_FRAMEON) != 0); break; } } return fResult; } //---------------------------------------------------------------------------// // // Edit_SetIMEMenu() // // support IME specific context menu // BOOL Edit_SetIMEMenu(HMENU hMenu, HWND hwnd, EditMenuItemState state) { MENUITEMINFO mii; HIMC hIMC; HKL hKL; HMENU hmenuSub; WCHAR szRes[32]; INT nPrevLastItem; INT nItemsAdded = 0; UserAssert(g_fIMMEnabled && state.fIME); hKL = GetKeyboardLayout(0); if (!ImmIsIME(hKL)) { return TRUE; } hIMC = ImmGetContext(hwnd); if (hIMC == NULL) { // // early out // return FALSE; } hmenuSub = GetSubMenu(hMenu, 0); if (hmenuSub == NULL) { return FALSE; } nPrevLastItem = GetMenuItemCount(hmenuSub); if (hIMC) { if (LOWORD(HandleToUlong(hKL)) != 0x412) { // // If Korean, do not show open/close menus // if (ImmGetOpenStatus(hIMC)) { LoadString(HINST_THISDLL, IDS_IMECLOSE, szRes, ARRAYSIZE(szRes)); } else { LoadString(HINST_THISDLL, IDS_IMEOPEN, szRes, ARRAYSIZE(szRes)); } mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_ID; mii.dwTypeData = szRes; mii.cch = 0xffff; mii.wID = ID_IMEOPENCLOSE; InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); ++nItemsAdded; } if (ImmGetProperty(hKL, IGP_CONVERSION) & IME_CMODE_SOFTKBD) { DWORD fdwConversion; if (ImmGetConversionStatus(hIMC, &fdwConversion, NULL) && (fdwConversion & IME_CMODE_SOFTKBD)) { LoadString(HINST_THISDLL, IDS_SOFTKBDCLOSE, szRes, ARRAYSIZE(szRes)); } else { LoadString(HINST_THISDLL, IDS_SOFTKBDOPEN, szRes, ARRAYSIZE(szRes)); } mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_ID; mii.dwTypeData = szRes; mii.cch = 0xffff; mii.wID = ID_SOFTKBDOPENCLOSE; InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); ++nItemsAdded; } if (LOWORD(HandleToUlong(hKL)) != 0x412) { // // If Korean, do not show reconversion menus // DWORD dwSCS = ImmGetProperty(hKL, IGP_SETCOMPSTR); LoadString(HINST_THISDLL, IDS_RECONVERTSTRING, szRes, ARRAYSIZE(szRes)); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE; mii.dwTypeData = szRes; mii.fState = 0; mii.cch = 0xffff; mii.wID = ID_RECONVERTSTRING; if (state.fDisableCut || !(dwSCS & SCS_CAP_SETRECONVERTSTRING) || !(dwSCS & SCS_CAP_MAKEREAD)) { mii.fState |= MFS_GRAYED; } InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); ++nItemsAdded; } } // // Add or remove the menu separator // if (state.fNeedSeparatorBeforeImeMenu && nItemsAdded != 0) { // // If the menu for Middle East has left a separator, // fNeedSeparatorBeforeImeMenu is FALSE. // I.e. we don't need to add more. // mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_FTYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem(hmenuSub, nPrevLastItem, TRUE, &mii); } else if (!state.fNeedSeparatorBeforeImeMenu && nItemsAdded == 0) { // // Extra separator is left by ME menus. Remove it. // DeleteMenu(hmenuSub, nPrevLastItem - 1, MF_BYPOSITION); } ImmReleaseContext(hwnd, hIMC); return TRUE; } //---------------------------------------------------------------------------// // VOID Edit_InOutReconversionMode(PED ped, BOOL fIn) { UserAssert(fIn == TRUE || fIn == FALSE); if (fIn != ped->fInReconversion) { ped->fInReconversion = fIn; if (ped->fFocus) { (fIn ? HideCaret: ShowCaret)(ped->hwnd); } } } //---------------------------------------------------------------------------// // BOOL Edit_EnumInputContextCB(HIMC hImc, LPARAM lParam) { DWORD dwConversion = 0, dwSentence = 0, dwNewConversion = 0; ImmGetConversionStatus(hImc, &dwConversion, &dwSentence); if (lParam) { dwNewConversion = dwConversion | IME_CMODE_SOFTKBD; } else { dwNewConversion = dwConversion & ~IME_CMODE_SOFTKBD; } if (dwNewConversion != dwConversion) { ImmSetConversionStatus(hImc, dwNewConversion, dwSentence); } return TRUE; } //---------------------------------------------------------------------------// // // Edit_DoIMEMenuCommand() // // support IME specific context menu // BOOL Edit_DoIMEMenuCommand(PED ped, int cmd, HWND hwnd) { HIMC hIMC; // early out switch (cmd) { case ID_IMEOPENCLOSE: case ID_SOFTKBDOPENCLOSE: case ID_RECONVERTSTRING: break; default: return FALSE; } // // everybody needs hIMC, so get it here // hIMC = ImmGetContext(hwnd); if (hIMC == NULL) { // // indicate to caller, that no further command processing needed // return TRUE; } switch (cmd) { case ID_IMEOPENCLOSE: { // switch IME Open/Close status BOOL fOpen = ImmGetOpenStatus(hIMC); ImmSetOpenStatus(hIMC, !fOpen); } break; case ID_SOFTKBDOPENCLOSE: { DWORD fdwConversion; if (ImmGetConversionStatus(hIMC, &fdwConversion, NULL)) { // // Toggle soft keyboard Open/Close status // ImmEnumInputContext(0, Edit_EnumInputContextCB, (fdwConversion & IME_CMODE_SOFTKBD) != IME_CMODE_SOFTKBD); } } break; case ID_RECONVERTSTRING: { DWORD dwStrLen; // holds TCHAR count of recionversion string DWORD cbLen; // holds BYTE SIZE of reconversion string DWORD dwSize; LPRECONVERTSTRING lpRCS; // // pass current selection to IME for reconversion // dwStrLen = ped->ichMaxSel - ped->ichMinSel; cbLen = dwStrLen * ped->cbChar; dwSize = cbLen + sizeof(RECONVERTSTRING) + 8; lpRCS = (LPRECONVERTSTRING)UserLocalAlloc(0, dwSize); if (lpRCS) { LPBYTE pText; ICH ichSelMinOrg; ichSelMinOrg = ped->ichMinSel; pText = Edit_Lock(ped); if (pText != NULL) { LPBYTE lpDest; BOOL (WINAPI* fpSetCompositionStringAW)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD); lpRCS->dwSize = dwSize; lpRCS->dwVersion = 0; lpRCS->dwStrLen = lpRCS->dwCompStrLen = lpRCS->dwTargetStrLen = dwStrLen; lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); lpRCS->dwCompStrOffset = lpRCS->dwTargetStrOffset = 0; lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING); RtlCopyMemory(lpDest, pText + ped->ichMinSel * ped->cbChar, cbLen); if (ped->fAnsi) { LPBYTE psz = (LPBYTE)lpDest; psz[cbLen] = '\0'; fpSetCompositionStringAW = ImmSetCompositionStringA; } else { LPWSTR pwsz = (LPWSTR)lpDest; pwsz[dwStrLen] = L'\0'; fpSetCompositionStringAW = ImmSetCompositionStringW; } Edit_Unlock(ped); UserAssert(fpSetCompositionStringAW != NULL); Edit_InOutReconversionMode(ped, TRUE); Edit_ImmSetCompositionWindow(ped, 0, 0); // x and y will be overriden anyway // Query the IME for a valid Reconvert string range first. fpSetCompositionStringAW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, dwSize, NULL, 0); // If current IME updates the original reconvert structure, // it is necessary to update the text selection based on the // new reconvert text range. if ((lpRCS->dwCompStrLen != dwStrLen) || (ichSelMinOrg != ped->ichMinSel)) { ICH ichSelStart; ICH ichSelEnd; ichSelStart = ichSelMinOrg + (lpRCS->dwCompStrOffset / ped->cbChar); ichSelEnd = ichSelStart + lpRCS->dwCompStrLen; (ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd); } fpSetCompositionStringAW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, dwSize, NULL, 0); } UserLocalFree(lpRCS); } break; } default: // // should never reach here. // TraceMsg(TF_STANDARD, "EDIT: Edit_DoIMEMenuCommand: unknown command id %d; should never reach here.", cmd); return FALSE; } UserAssert(hIMC != NULL); ImmReleaseContext(hwnd, hIMC); return TRUE; } //---------------------------------------------------------------------------// // // Edit_Menu() // // Handles context menu for edit fields. Disables inappropriate commands. // Note that this is NOT subclassing friendly, like most of our functions, // for speed and convenience. // VOID Edit_Menu(HWND hwnd, PED ped, LPPOINT pt) { HMENU hMenu; INT cmd = 0; INT x; INT y; UINT uFlags = TPM_NONOTIFY | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON; EditMenuItemState state = { FALSE, // fDisableCut TRUE, // fDisablePaste TRUE, // fNeedSeparatorBeforeImeMenu g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0)), // fIME }; // // Set focus if we don't have its // if (!ped->fFocus) { SetFocus(hwnd); } // // Grab the menu from our resources... // hMenu = LoadMenu( HINST_THISDLL, MAKEINTRESOURCE( ID_EC_PROPERTY_MENU )); if (hMenu) { // // Undo -- not allowed if we have no saved undo info // if (ped->undoType == UNDO_NONE) { EnableMenuItem(hMenu, WM_UNDO, MF_BYCOMMAND | MFS_GRAYED); } if (ped->fReadOnly || ped->charPasswordChar) { // // Cut and Delete -- not allowed if read-only or password // state.fDisableCut = TRUE; } else { // // Cut, Delete -- not allowed if there's no selection // if (ped->ichMinSel == ped->ichMaxSel) { state.fDisableCut = TRUE; } } // // Paste -- not allowed if there's no text on the clipboard // (this works for both OEM and Unicode) // Used to be always disabled for password edits MCostea #221035 // if (IsClipboardFormatAvailable(CF_TEXT)) { state.fDisablePaste = FALSE; } if (state.fDisableCut) { EnableMenuItem(hMenu, WM_CUT, MF_BYCOMMAND | MFS_GRAYED); EnableMenuItem(hMenu, WM_CLEAR, MF_BYCOMMAND | MFS_GRAYED); } if (state.fDisablePaste) { EnableMenuItem(hMenu, WM_PASTE, MF_BYCOMMAND | MFS_GRAYED); } // // Copy -- not allowed if there's no selection or password ec // if ((ped->ichMinSel == ped->ichMaxSel) || (ped->charPasswordChar)) { EnableMenuItem(hMenu, WM_COPY, MF_BYCOMMAND | MFS_GRAYED); } // // Select All -- not allowed if there's no text or if everything is // selected. Latter case takes care of first one. // if ((ped->ichMinSel == 0) && (ped->ichMaxSel == ped->cch)) { EnableMenuItem(hMenu, EM_SETSEL, MF_BYCOMMAND | MFS_GRAYED); } if (ped->pLpkEditCallout) { ped->pLpkEditCallout->EditSetMenu((PED0)ped, hMenu); } else { DeleteMenu(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND); DeleteMenu(hMenu, ID_CNTX_RTL, MF_BYCOMMAND); DeleteMenu(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND); if (state.fIME) { // // One separator is left in the menu, // no need to add the one before IME menus // state.fNeedSeparatorBeforeImeMenu = FALSE; } else { // // Extra separator is left. Remove it. // HMENU hmenuSub = GetSubMenu(hMenu, 0); INT nItems = GetMenuItemCount(hmenuSub) - 1; UserAssert(nItems >= 0); UserAssert(GetMenuState(hmenuSub, nItems, MF_BYPOSITION) & MF_SEPARATOR); // // remove needless separator // DeleteMenu(hmenuSub, nItems, MF_BYPOSITION); } } // // IME specific menu // if (state.fIME) { Edit_SetIMEMenu(hMenu, hwnd, state); } // // BOGUS // We position the menu below & to the right of the point clicked on. // Is this cool? I think so. Excel 4.0 does the same thing. It // seems like it would be neat if we could avoid obscuring the // selection. But in actuality, it seems even more awkward to move // the menu out of the way of the selection. The user can't click // and drag that way, and they have to move the mouse a ton. // // We need to use TPM_NONOTIFY because VBRUN100 and VBRUN200 GP-fault // on unexpected menu messages. // // // if message came via the keyboard then center on the control // We use -1 && -1 here not 0xFFFFFFFF like Win95 becuase we // previously converted the lParam to a point with sign extending. // if (pt->x == -1 && pt->y == -1) { RECT rc; GetWindowRect(hwnd, &rc); x = rc.left + (rc.right - rc.left) / 2; y = rc.top + (rc.bottom - rc.top) / 2; } else { x = pt->x; y = pt->y; } if ( IS_BIDI_LOCALIZED_SYSTEM() ) { uFlags |= TPM_LAYOUTRTL; } cmd = TrackPopupMenuEx(GetSubMenu(hMenu, 0), uFlags, x, y, hwnd, NULL); // // Free our menu // DestroyMenu(hMenu); if (cmd && (cmd != -1)) { if (ped->pLpkEditCallout && cmd) { ped->pLpkEditCallout->EditProcessMenu((PED0)ped, cmd); } if (!state.fIME || !Edit_DoIMEMenuCommand(ped, cmd, hwnd)) { // // if cmd is not IME specific menu, send it. // SendMessage(hwnd, cmd, 0, (cmd == EM_SETSEL) ? 0xFFFFFFFF : 0L ); } } } } //---------------------------------------------------------------------------// // // Edit_ClearText() // // Clears selected text. Does NOT _send_ a fake char backspace. // VOID Edit_ClearText(PED ped) { if (!ped->fReadOnly && (ped->ichMinSel < ped->ichMaxSel)) { if (ped->fSingle) { EditSL_WndProc(ped, WM_CHAR, VK_BACK, 0L ); } else { EditML_WndProc(ped, WM_CHAR, VK_BACK, 0L ); } } } //---------------------------------------------------------------------------// // // Edit_CutText() // // Cuts selected text. This removes and copies the selection to the clip, // or if nothing is selected we delete (clear) the left character. // VOID Edit_CutText(PED ped) { // // Cut selection--IE, remove and copy to clipboard, or if no selection, // delete (clear) character left. // if (!ped->fReadOnly && (ped->ichMinSel < ped->ichMaxSel) && SendMessage(ped->hwnd, WM_COPY, 0, 0L)) { // // If copy was successful, delete the copied text by sending a // backspace message which will redraw the text and take care of // notifying the parent of changes. // Edit_ClearText(ped); } } //---------------------------------------------------------------------------// // // Edit_GetModKeys() // // Gets modifier key states. Currently, we only check for VK_CONTROL and // VK_SHIFT. // INT Edit_GetModKeys(INT keyMods) { INT scState; scState = 0; if (!keyMods) { if (GetKeyState(VK_CONTROL) < 0) { scState |= CTRLDOWN; } if (GetKeyState(VK_SHIFT) < 0) { scState |= SHFTDOWN; } } else if (keyMods != NOMODIFY) { scState = keyMods; } return scState; } //---------------------------------------------------------------------------// // // Edit_TabTheTextOut() AorW // If fDrawText == FALSE, then this function returns the text extent of // of the given strip of text. It does not worry about the Negative widths. // // If fDrawText == TRUE, this draws the given strip of Text expanding the // tabs to proper lengths, calculates and fills up the NegCInfoForStrip with // details required to draw the portions of this strip that goes beyond the // xClipEndPos due to Negative C widths. // // Returns the max width AS A DWORD. We don't care about the height // at all. No one uses it. We keep a DWORD because that way we avoid // overflow. // // NOTE: If the language pack is loaded EcTabTheTextOut is not used - the // language pack must take care of all tab expansion and selection // highlighting with full support for bidi layout and complex script // glyph reordering. // UINT Edit_TabTheTextOut( HDC hdc, INT xClipStPos, INT xClipEndPos, INT xStart, INT y, LPSTR lpstring, INT nCount, ICH ichString, PED ped, INT iTabOrigin, BOOL fDraw, LPSTRIPINFO NegCInfoForStrip) { INT nTabPositions; // Count of tabstops in tabstop array. LPINT lpintTabStopPositions; // Tab stop positions in pixels. INT cch; UINT textextent; INT xEnd; INT pixeltabstop = 0; INT i; INT cxCharWidth; RECT rc; BOOL fOpaque; BOOL fFirstPass = TRUE; PINT charWidthBuff; INT iTabLength; INT nConsecutiveTabs; INT xStripStPos; INT xStripEndPos; INT xEndOfStrip; STRIPINFO RedrawStripInfo; STRIPINFO NegAInfo; LPSTR lpTab; LPWSTR lpwTab; UINT wNegCwidth, wNegAwidth; INT xRightmostPoint = xClipStPos; INT xTabStartPos; INT iSavedBkMode = 0; WCHAR wchar; SIZE size = {0}; ABC abc ; COLORREF clrBkSave; COLORREF clrTextSave; HBRUSH hbrBack = NULL; BOOL fNeedDelete = FALSE; HRESULT hr = E_FAIL; UINT uRet; // // Algorithm: Draw the strip opaquely first. If a tab length is so // small that the portions of text on either side of a tab overlap with // the other, then this will result in some clipping. So, such portion // of the strip is remembered in "RedrawStripInfo" and redrawn // transparently later to compensate the clippings. // NOTE: "RedrawStripInfo" can hold info about just one portion. So, if // more than one portion of the strip needs to be redrawn transparently, // then we "merge" all such portions into a single strip and redraw that // strip at the end. // if (fDraw) { // // To begin with, let us assume that there is no Negative C for this // strip and initialize the Negative Width Info structure. // NegCInfoForStrip->nCount = 0; NegCInfoForStrip->XStartPos = xClipEndPos; // // We may not have to redraw any portion of this strip. // RedrawStripInfo.nCount = 0; fOpaque = (GetBkMode(hdc) == OPAQUE) || (fDraw == ECT_SELECTED); } #if DBG else { // // Both EditML_GetLineWidth() and Edit_CchInWidth() should be clipping // nCount to avoid overflow. // if (nCount > MAXLINELENGTH) { TraceMsg(TF_STANDARD, "EDIT: Edit_TabTheTextOut: %d > MAXLINELENGTH", nCount); } } #endif // // Let us define the Clip rectangle. // rc.left = xClipStPos; rc.right = xClipEndPos; rc.top = y; rc.bottom = y + ped->lineHeight; #ifdef _USE_DRAW_THEME_TEXT_ // // Check if we are themed. // if (ped->hTheme) { COLORREF clrBk; COLORREF clrText; INT iState; INT iProp; iState = (fDraw == ECT_SELECTED) ? ETS_SELECTED : Edit_GetStateId(ped); iProp = (fDraw == ECT_SELECTED) ? TMT_HIGHLIGHT : TMT_FILLCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrBk); if (SUCCEEDED(hr)) { iProp = (fDraw == ECT_SELECTED) ? TMT_HIGHLIGHTTEXT : TMT_TEXTCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrText); if (SUCCEEDED(hr)) { hbrBack = CreateSolidBrush(clrBk); fNeedDelete = TRUE; clrBkSave = SetBkColor(hdc, clrBk); clrTextSave = SetTextColor(hdc, clrText); } } } #endif // _USE_DRAW_THEME_TEXT_ if (!ped->hTheme || FAILED(hr)) { if (fDraw == ECT_SELECTED) { // // use normal colors // hbrBack = GetSysColorBrush(COLOR_HIGHLIGHT); clrBkSave = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); clrTextSave = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { hbrBack = Edit_GetBrush(ped, hdc, &fNeedDelete); clrBkSave = GetBkColor(hdc); clrTextSave = GetTextColor(hdc); } } // // Check if anything needs to be drawn. // if (!lpstring || !nCount) { if (fDraw) { ExtTextOutW(hdc, xClipStPos, y, (fOpaque ? ETO_OPAQUE | ETO_CLIPPED : ETO_CLIPPED), &rc, L"", 0, 0L); } uRet = 0; } else { // // Starting position // xEnd = xStart; cxCharWidth = ped->aveCharWidth; nTabPositions = (ped->pTabStops ? *(ped->pTabStops) : 0); if (ped->pTabStops) { lpintTabStopPositions = (LPINT)(ped->pTabStops+1); if (nTabPositions == 1) { pixeltabstop = lpintTabStopPositions[0]; if (!pixeltabstop) { pixeltabstop = 1; } } } else { lpintTabStopPositions = NULL; pixeltabstop = 8*cxCharWidth; } // // The first time we will draw the strip Opaquely. If some portions need // to be redrawn , then we will set the mode to TRANSPARENT and // jump to this location to redraw those portions. // RedrawStrip: while (nCount) { wNegCwidth = ped->wMaxNegC; // // Search for the first TAB in this strip; also compute the extent // of the the strip upto and not including the tab character. // // Note - If the langpack is loaded, there will be no charWidthBuffer. // // // Do we have a character width buffer? // if (ped->charWidthBuffer) { textextent = 0; cch = nCount; // // If so, does it have ABC widths? // if (ped->fTrueType) { UINT iRightmostPoint = 0; UINT wCharIndex; PABC pABCwidthBuff; pABCwidthBuff = (PABC) ped->charWidthBuffer; if (ped->fAnsi) { for (i = 0; i < nCount; i++) { if (lpstring[i] == VK_TAB) { cch = i; break; } wCharIndex = (UINT)(((PUCHAR)lpstring)[i]); if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { textextent += (UINT)(pABCwidthBuff[wCharIndex].abcA + pABCwidthBuff[wCharIndex].abcB); } else { // // not in cache, will ask driver // GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc); textextent += abc.abcA + abc.abcB ; } if (textextent > iRightmostPoint) { iRightmostPoint = textextent; } if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { textextent += pABCwidthBuff[wCharIndex].abcC; } else { // // not in cache // textextent += abc.abcC; } if (textextent > iRightmostPoint) { iRightmostPoint = textextent; } } } else { for (i = 0; i < nCount; i++) { WCHAR UNALIGNED * lpwstring = (WCHAR UNALIGNED *)lpstring; if (lpwstring[i] == VK_TAB) { cch = i; break; } wCharIndex = lpwstring[i] ; if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) { textextent += pABCwidthBuff[wCharIndex].abcA + pABCwidthBuff[wCharIndex].abcB; } else { GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ; textextent += abc.abcA + abc.abcB ; } // // Note that abcC could be negative so we need this // statement here *and* below // if (textextent > iRightmostPoint) { iRightmostPoint = textextent; } if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) { textextent += pABCwidthBuff[wCharIndex].abcC; } else { textextent += abc.abcC ; } if (textextent > iRightmostPoint) { iRightmostPoint = textextent; } } } wNegCwidth = (int)(iRightmostPoint - textextent); } else { // // No! This is not a TrueType font; So, we have only character // width info in this buffer. // charWidthBuff = ped->charWidthBuffer; if (ped->fAnsi) { // // Initially assume no tabs exist in the text so cch=nCount. // for (i = 0; i < nCount; i++) { if (lpstring[i] == VK_TAB) { cch = i; break; } // // Call GetTextExtentPoint for dbcs/hankaku characters // if (ped->fDBCS && (i+1 < nCount) && Edit_IsDBCSLeadByte(ped,lpstring[i])) { GetTextExtentPointA(hdc, &lpstring[i], 2, &size); textextent += size.cx; i++; } else if ((UCHAR)lpstring[i] >= CHAR_WIDTH_BUFFER_LENGTH) { // // Skip this GetExtentPoint call for non hankaku code points // Or if the character is in the width cache. // GetTextExtentPointA(hdc, &lpstring[i], 1, &size); textextent += size.cx; } else { textextent += (UINT)(charWidthBuff[(UINT)(((PUCHAR)lpstring)[i])]); } } } else { LPWSTR lpwstring = (LPWSTR) lpstring ; INT cchUStart; // start of unicode character count for (i = 0; i < nCount; i++) { if (lpwstring[i] == VK_TAB) { cch = i; break; } wchar = lpwstring[i]; if (wchar >= CHAR_WIDTH_BUFFER_LENGTH) { // // We have a Unicode character that is not in our // cache, get all the characters outside the cache // before getting the text extent on this part of the // string. // cchUStart = i; while (wchar >= CHAR_WIDTH_BUFFER_LENGTH && wchar != VK_TAB && i < nCount) { wchar = lpwstring[++i]; } GetTextExtentPointW(hdc, (LPWSTR)lpwstring + cchUStart, i-cchUStart, &size); textextent += size.cx; if (wchar == VK_TAB || i >= nCount) { cch = i; break; } // // We have a char that is in the cache, fall through. // } // // The width of this character is in the cache buffer. // textextent += ped->charWidthBuffer[wchar]; } } } nCount -= cch; } else { // // Gotta call the driver to do our text extent. // if (ped->fAnsi) { cch = (INT)Edit_FindTabA(lpstring, nCount); GetTextExtentPointA(hdc, lpstring, cch, &size); } else { cch = (INT)Edit_FindTabW((LPWSTR) lpstring, nCount); GetTextExtentPointW(hdc, (LPWSTR)lpstring, cch, &size); } nCount -= cch; // // Subtruct Overhang for Italic fonts. // textextent = size.cx - ped->charOverhang; } // // textextent is computed. // xStripStPos = xEnd; xEnd += (int)textextent; xStripEndPos = xEnd; // // We will consider the negative widths only if when we draw opaquely. // if (fFirstPass && fDraw) { xRightmostPoint = max(xStripEndPos + (int)wNegCwidth, xRightmostPoint); // // Check if this strip peeps beyond the clip region. // if (xRightmostPoint > xClipEndPos) { if (!NegCInfoForStrip->nCount) { NegCInfoForStrip->lpString = lpstring; NegCInfoForStrip->ichString = ichString; NegCInfoForStrip->nCount = nCount+cch; NegCInfoForStrip->XStartPos = xStripStPos; } } } if (ped->fAnsi) { // // Possibly Points to a tab character. // lpTab = lpstring + cch; } else { lpwTab = ((LPWSTR)lpstring) + cch ; } // // we must consider all the consecutive tabs and calculate the // the begining of next strip. // nConsecutiveTabs = 0; while (nCount && (ped->fAnsi ? (*lpTab == VK_TAB) : (*lpwTab == VK_TAB))) { // // Find the next tab position and update the x value. // xTabStartPos = xEnd; if (pixeltabstop) { xEnd = (((xEnd-iTabOrigin)/pixeltabstop)*pixeltabstop) + pixeltabstop + iTabOrigin; } else { for (i = 0; i < nTabPositions; i++) { if (xEnd < (lpintTabStopPositions[i] + iTabOrigin)) { xEnd = (lpintTabStopPositions[i] + iTabOrigin); break; } } // // Check if all the tabstops set are exhausted; Then start using // default tab stop positions. // if (i == nTabPositions) { pixeltabstop = 8*cxCharWidth; xEnd = ((xEnd - iTabOrigin)/pixeltabstop)*pixeltabstop + pixeltabstop + iTabOrigin; } } if (fFirstPass && fDraw) { xRightmostPoint = max(xEnd, xRightmostPoint); // // Check if this strip peeps beyond the clip region // if (xRightmostPoint > xClipEndPos) { if (!NegCInfoForStrip->nCount) { NegCInfoForStrip->ichString = ichString + cch + nConsecutiveTabs; NegCInfoForStrip->nCount = nCount; NegCInfoForStrip->lpString = (ped->fAnsi ? lpTab : (LPSTR) lpwTab); NegCInfoForStrip->XStartPos = xTabStartPos; } } } nConsecutiveTabs++; nCount--; ped->fAnsi ? lpTab++ : (LPSTR) (lpwTab++) ; // Move to the next character. } if (fDraw) { if (fFirstPass) { // // Is anything remaining to be drawn in this strip? // if (!nCount) { // // No! We are done. // rc.right = xEnd; } else { // // "x" is the effective starting position of next strip. // iTabLength = xEnd - xStripEndPos; // // Check if there is a possibility of this tab length being too small // compared to the negative A and C widths if any. // if ((wNegCwidth + (wNegAwidth = ped->wMaxNegA)) > (UINT)iTabLength) { // // Unfortunately, there is a possiblity of an overlap. // Let us find out the actual NegA for the next strip. // wNegAwidth = GetActualNegA( hdc, ped, xEnd, lpstring + (cch + nConsecutiveTabs)*ped->cbChar, ichString + cch + nConsecutiveTabs, nCount, &NegAInfo); } // // Check if they actually overlap // if ((wNegCwidth + wNegAwidth) <= (UINT)iTabLength) { // // No overlap between the strips. This is the ideal situation. // rc.right = xEnd - wNegAwidth; } else { // // Yes! They overlap. // rc.right = xEnd; // // See if negative C width is too large compared to tab length. // if (wNegCwidth > (UINT)iTabLength) { // // Must redraw transparently a part of the current strip later. // if (RedrawStripInfo.nCount) { // // A previous strip also needs to be redrawn; So, merge this // strip to that strip. // RedrawStripInfo.nCount = (ichString - RedrawStripInfo.ichString) + cch; } else { RedrawStripInfo.nCount = cch; RedrawStripInfo.lpString = lpstring; RedrawStripInfo.ichString = ichString; RedrawStripInfo.XStartPos = xStripStPos; } } if (wNegAwidth) { // // Must redraw transparently the first part of the next strip later. // if (RedrawStripInfo.nCount) { // // A previous strip also needs to be redrawn; So, merge this // strip to that strip. // RedrawStripInfo.nCount = (NegAInfo.ichString - RedrawStripInfo.ichString) + NegAInfo.nCount; } else { RedrawStripInfo = NegAInfo; } } } } } if (rc.left < xClipEndPos) { if (fFirstPass) { // // If this is the end of the strip, then complete the rectangle. // if ((!nCount) && (xClipEndPos == MAXCLIPENDPOS)) { rc.right = max(rc.right, xClipEndPos); } else { rc.right = min(rc.right, xClipEndPos); } } // // Draw the current strip. // if (rc.left < rc.right) { if (ped->fAnsi) { ExtTextOutA(hdc, xStripStPos, y, (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED), (LPRECT)&rc, lpstring, cch, 0L); } else { ExtTextOutW(hdc, xStripStPos, y, (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED), (LPRECT)&rc, (LPWSTR)lpstring, cch, 0L); } } } if (fFirstPass) { rc.left = max(rc.right, xClipStPos); } ichString += (cch+nConsecutiveTabs); } // // Skip over the tab and the characters we just drew. // lpstring += (cch + nConsecutiveTabs) * ped->cbChar; } xEndOfStrip = xEnd; // // check if we need to draw some portions transparently. // if (fFirstPass && fDraw && RedrawStripInfo.nCount) { iSavedBkMode = SetBkMode(hdc, TRANSPARENT); fFirstPass = FALSE; nCount = RedrawStripInfo.nCount; rc.left = xClipStPos; rc.right = xClipEndPos; lpstring = RedrawStripInfo.lpString; ichString = RedrawStripInfo.ichString; xEnd = RedrawStripInfo.XStartPos; // // Redraw Transparently. // goto RedrawStrip; } // // Did we change the Bk mode? // if (iSavedBkMode) { SetBkMode(hdc, iSavedBkMode); } uRet = (UINT)(xEndOfStrip - xStart); } SetTextColor(hdc, clrTextSave); SetBkColor(hdc, clrBkSave); if (hbrBack && fNeedDelete) { DeleteObject(hbrBack); } return uRet; } //---------------------------------------------------------------------------// // // Edit_CchInWidth AorW // // Returns maximum count of characters (up to cch) from the given // string (starting either at the beginning and moving forward or at the // end and moving backwards based on the setting of the fForward flag) // which will fit in the given width. ie. Will tell you how much of // lpstring will fit in the given width even when using proportional // characters. WARNING: If we use kerning, then this loses... // // NOTE: Edit_CchInWidth is not called if the language pack is loaded. // ICH Edit_CchInWidth( PED ped, HDC hdc, LPSTR lpText, ICH cch, INT width, BOOL fForward) { INT stringExtent; INT cchhigh; INT cchnew = 0; INT cchlow = 0; SIZE size; LPSTR lpStart; if ((width <= 0) || !cch) { return (0); } // // Optimize nonproportional fonts for single line ec since they don't have // tabs. // // // Change optimize condition for fixed pitch font // if (ped->fNonPropFont && ped->fSingle && !ped->fDBCS) { return Edit_AdjustIch(ped, lpText, umin(width/ped->aveCharWidth, (INT)cch)); } // // Check if password hidden chars are being used. // if (ped->charPasswordChar) { return (umin(width / ped->cPasswordCharWidth, (INT)cch)); } // // ALWAYS RESTRICT TO AT MOST MAXLINELENGTH to avoid overflow... // cch = umin(MAXLINELENGTH, cch); cchhigh = cch + 1; while (cchlow < cchhigh - 1) { cchnew = umax((cchhigh - cchlow) / 2, 1) + cchlow; lpStart = lpText; // // If we want to figure out how many fit starting at the end and moving // backwards, make sure we move to the appropriate position in the // string before calculating the text extent. // if (!fForward) { lpStart += (cch - cchnew)*ped->cbChar; } if (ped->fSingle) { if (ped->fAnsi) { GetTextExtentPointA(hdc, (LPSTR)lpStart, cchnew, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)lpStart, cchnew, &size); } stringExtent = size.cx; } else { stringExtent = Edit_TabTheTextOut(hdc, 0, 0, 0, 0, lpStart, cchnew, 0, ped, 0, ECT_CALC, NULL ); } if (stringExtent > width) { cchhigh = cchnew; } else { cchlow = cchnew; } } // // Call Edit_AdjustIch ( generic case ) // cchlow = Edit_AdjustIch(ped, lpText, cchlow); return cchlow; } //---------------------------------------------------------------------------// // // Edit_FindTab // // Scans lpstr and return s the number of CHARs till the first TAB. // Scans at most cch chars of lpstr. // ICH Edit_FindTabA( LPSTR lpstr, ICH cch) { LPSTR copylpstr = lpstr; if (cch) { while (*lpstr != VK_TAB) { lpstr++; if (--cch == 0) { break; } } } return (ICH)(lpstr - copylpstr); } //---------------------------------------------------------------------------// // ICH Edit_FindTabW( LPWSTR lpstr, ICH cch) { LPWSTR copylpstr = lpstr; if (cch) { while (*lpstr != VK_TAB) { lpstr++; if (--cch == 0) { break; } } } return ((ICH)(lpstr - copylpstr)); } //---------------------------------------------------------------------------// // // Edit_GetBrush() // // Gets appropriate background brush to erase with. // HBRUSH Edit_GetBrush(PED ped, HDC hdc, LPBOOL pfNeedDelete) { HBRUSH hbr; COLORREF clr; HRESULT hr = E_FAIL; #ifdef _USE_DRAW_THEME_TEXT_ if (ped->hTheme) { INT iStateId = Edit_GetStateId(ped); hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iStateId, TMT_FILLCOLOR, &clr); if (SUCCEEDED(hr)) { hbr = CreateSolidBrush(clr); if (pfNeedDelete) { // // tell the caller this brush needs to be deleted // *pfNeedDelete = TRUE; } } } #endif // _USE_DRAW_THEME_TEXT_ if (!ped->hTheme || FAILED(hr)) { BOOL f40Compat; f40Compat = Is400Compat(UserGetVersion()); // // Get background brush // if ((ped->fReadOnly || ped->fDisabled) && f40Compat) { hbr = Edit_GetControlBrush(ped, hdc, WM_CTLCOLORSTATIC); } else { hbr = Edit_GetControlBrush(ped, hdc, WM_CTLCOLOREDIT); } if (ped->fDisabled && (ped->fSingle || f40Compat)) { // // Change text color // clr = GetSysColor(COLOR_GRAYTEXT); if (clr != GetBkColor(hdc)) { SetTextColor(hdc, clr); } } } return hbr; } //---------------------------------------------------------------------------// // // NextWordCallBack // VOID NextWordCallBack(PED ped, ICH ichStart, BOOL fLeft, ICH *pichMin, ICH *pichMax) { ICH ichMinSel; ICH ichMaxSel; LPSTR pText; pText = Edit_Lock(ped); if (fLeft || (!(BOOL)CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_ISDELIMITER) && (ped->fAnsi ? (*(pText + ichStart) != VK_RETURN) : (*((LPWSTR)pText + ichStart) != VK_RETURN)))) { ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_LEFT); } else { ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_RIGHT); } ichMaxSel = min(ichMinSel + 1, ped->cch); if (ped->fAnsi) { if (*(pText + ichMinSel) == VK_RETURN) { if (ichMinSel > 0 && *(pText + ichMinSel - 1) == VK_RETURN) { // // So that we can treat CRCRLF as one word also. // ichMinSel--; } else if (*(pText+ichMinSel + 1) == VK_RETURN) { // // Move MaxSel on to the LF // ichMaxSel++; } } } else { if (*((LPWSTR)pText + ichMinSel) == VK_RETURN) { if (ichMinSel > 0 && *((LPWSTR)pText + ichMinSel - 1) == VK_RETURN) { // // So that we can treat CRCRLF as one word also. // ichMinSel--; } else if (*((LPWSTR)pText+ichMinSel + 1) == VK_RETURN) { // // Move MaxSel on to the LF // ichMaxSel++; } } } ichMaxSel = CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichMaxSel, ped->cch, WB_RIGHT); Edit_Unlock(ped); if (pichMin) { *pichMin = ichMinSel; } if (pichMax) { *pichMax = ichMaxSel; } } //---------------------------------------------------------------------------// // // NextWordLpkCallback // // Identifies next/prev word position for complex scripts // VOID NextWordLpkCallBack(PED ped, ICH ichStart, BOOL fLeft, ICH *pichMin, ICH *pichMax) { PSTR pText = Edit_Lock(ped); HDC hdc = Edit_GetDC(ped, TRUE); ped->pLpkEditCallout->EditNextWord((PED0)ped, hdc, pText, ichStart, fLeft, pichMin, pichMax); Edit_ReleaseDC(ped, hdc, TRUE); Edit_Unlock(ped); } //---------------------------------------------------------------------------// // // Edit_Word // // if fLeft, Returns the ichMinSel and ichMaxSel of the word to the // left of ichStart. ichMinSel contains the starting letter of the word, // ichmaxsel contains all spaces up to the first character of the next word. // // if !fLeft, Returns the ichMinSel and ichMaxSel of the word to the right of // ichStart. ichMinSel contains the starting letter of the word, ichmaxsel // contains the first letter of the next word. If ichStart is in the middle // of a word, that word is considered the left or right word. // // A CR LF pair or CRCRLF triple is considered a single word in // multiline edit controls. // VOID Edit_Word(PED ped, ICH ichStart, BOOL fLeft, LPICH pichMin, LPICH pichMax) { BOOL charLocated = FALSE; BOOL spaceLocated = FALSE; if ((!ichStart && fLeft) || (ichStart == ped->cch && !fLeft)) { // // We are at the beginning of the text (looking left) or we are at end // of text (looking right), no word here // if (pichMin) { *pichMin = 0; } if (pichMax) { *pichMax = 0; } return; } // // Don't give out hints about word breaks if password chars are being used, // if (ped->charPasswordChar) { if (pichMin) { *pichMin = 0; } if (pichMax) { *pichMax = ped->cch; } return; } if (ped->fAnsi) { PSTR pText; PSTR pWordMinSel; PSTR pWordMaxSel; PSTR pPrevChar; UserAssert(ped->cbChar == sizeof(CHAR)); if (ped->lpfnNextWord) { NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax); return; } if (ped->pLpkEditCallout) { NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax); return; } pText = Edit_Lock(ped); pWordMinSel = pWordMaxSel = pText + ichStart; // // if fLeft: Move pWordMinSel to the left looking for the start of a word. // If we start at a space, we will include spaces in the selection as we // move left untill we find a nonspace character. At that point, we continue // looking left until we find a space. Thus, the selection will consist of // a word with its trailing spaces or, it will consist of any leading at the // beginning of a line of text. // // // if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a // word. If the pWordMinSel points to a character, then we move left // looking for a space which will signify the start of the word. If // pWordMinSel points to a space, we look right till we come upon a // character. pMaxWord will look right starting at pMinWord looking for the // end of the word and its trailing spaces. // if (fLeft || !ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0D) { // // If we are moving left or if we are moving right and we are not on a // space or a CR (the start of a word), then we was look left for the // start of a word which is either a CR or a character. We do this by // looking left till we find a character (or if CR we stop), then we // continue looking left till we find a space or LF. // while (pWordMinSel > pText && ((!ISDELIMETERA(*(pWordMinSel - 1)) && *(pWordMinSel - 1) != 0x0A) || !charLocated)) { // // Treat double byte character as a word ( in ansi pWordMinSel loop ) // pPrevChar = Edit_AnsiPrev( ped, pText, pWordMinSel ); // // are looking right ( !fLeft ). // current character is a double byte chararacter or // character is a double byte character, we // on the beggining of a word. // if ( !fLeft && ( ISDELIMETERA( *pPrevChar ) || *pPrevChar == 0x0A || Edit_IsDBCSLeadByte(ped, *pWordMinSel) || pWordMinSel - pPrevChar == 2 ) ) { // // If we are looking for the start of the word right, then we // stop when we have found it. (needed in case charLocated is // still FALSE) // break; } if (pWordMinSel - pPrevChar == 2) { // // character is a double byte character. // we are in a word ( charLocated == TRUE ) // position is the beginning of the word // we are not in a word ( charLocated == FALSE ) // previous character is what we looking for. // if (!charLocated) { pWordMinSel = pPrevChar; } break; } pWordMinSel = pPrevChar; if (!ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0A) { // // We have found the last char in the word. Continue looking // backwards till we find the first char of the word // charLocated = TRUE; // // We will consider a CR the start of a word // if (*pWordMinSel == 0x0D) { break; } } } } else { while ((ISDELIMETERA(*pWordMinSel) || *pWordMinSel == 0x0A) && pWordMinSel < pText + ped->cch) { pWordMinSel++; } } // // Adjust the initial position of pWordMaxSel ( in ansi ) // pWordMaxSel = Edit_AnsiNext(ped, pWordMinSel); pWordMaxSel = min(pWordMaxSel, pText + ped->cch); // // pWordMinSel points a double byte character AND // points non space // then // pWordMaxSel points the beggining of next word. // if ((pWordMaxSel - pWordMinSel == 2) && !ISDELIMETERA(*pWordMaxSel)) { goto FastReturnA; } if (*pWordMinSel == 0x0D) { if (pWordMinSel > pText && *(pWordMinSel - 1) == 0x0D) { // // So that we can treat CRCRLF as one word also. // pWordMinSel--; } else if (*(pWordMinSel + 1) == 0x0D) { // // Move MaxSel on to the LF // pWordMaxSel++; } } // // Check if we have a one character word // if (ISDELIMETERA(*pWordMaxSel)) { spaceLocated = TRUE; } // // Move pWordMaxSel to the right looking for the end of a word and its // trailing spaces. WordMaxSel stops on the first character of the next // word. Thus, we break either at a CR or at the first nonspace char after // a run of spaces or LFs. // while ((pWordMaxSel < pText + ped->cch) && (!spaceLocated || (ISDELIMETERA(*pWordMaxSel)))) { if (*pWordMaxSel == 0x0D) { break; } // // Treat double byte character as a word ( in ansi pWordMaxSel loop ) // if it's a double byte character then // we are at the beginning of next word // which is a double byte character. // if (Edit_IsDBCSLeadByte( ped, *pWordMaxSel)) { break; } pWordMaxSel++; if (ISDELIMETERA(*pWordMaxSel)) { spaceLocated = TRUE; } if (*(pWordMaxSel - 1) == 0x0A) { break; } } // // label for fast return ( for Ansi ) // FastReturnA: Edit_Unlock(ped); if (pichMin) { *pichMin = (ICH)(pWordMinSel - pText); } if (pichMax) { *pichMax = (ICH)(pWordMaxSel - pText); } } else { LPWSTR pwText; LPWSTR pwWordMinSel; LPWSTR pwWordMaxSel; BOOL charLocated = FALSE; BOOL spaceLocated = FALSE; PWSTR pwPrevChar; UserAssert(ped->cbChar == sizeof(WCHAR)); if (ped->lpfnNextWord) { NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax); return; } if (ped->pLpkEditCallout) { NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax); return; } pwText = (LPWSTR)Edit_Lock(ped); pwWordMinSel = pwWordMaxSel = pwText + ichStart; // // if fLeft: Move pWordMinSel to the left looking for the start of a word. // If we start at a space, we will include spaces in the selection as we // move left untill we find a nonspace character. At that point, we continue // looking left until we find a space. Thus, the selection will consist of // a word with its trailing spaces or, it will consist of any leading at the // beginning of a line of text. // // // if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a // word. If the pWordMinSel points to a character, then we move left // looking for a space which will signify the start of the word. If // pWordMinSel points to a space, we look right till we come upon a // character. pMaxWord will look right starting at pMinWord looking for the // end of the word and its trailing spaces. // if (fLeft || (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0D)) { // // If we are moving left or if we are moving right and we are not on a // space or a CR (the start of a word), then we was look left for the // start of a word which is either a CR or a character. We do this by // looking left till we find a character (or if CR we stop), then we // // continue looking left till we find a space or LF. while (pwWordMinSel > pwText && ((!ISDELIMETERW(*(pwWordMinSel - 1)) && *(pwWordMinSel - 1) != 0x0A) || !charLocated)) { // // Treat double byte character as a word ( in unicode pwWordMinSel loop ) // pwPrevChar = pwWordMinSel - 1; // // we are looking right ( !fLeft ). // // if current character is a double width chararacter // or previous character is a double width character, // we are on the beggining of a word. // if (!fLeft && (ISDELIMETERW( *pwPrevChar) || *pwPrevChar == 0x0A || Edit_IsFullWidth(CP_ACP,*pwWordMinSel) || Edit_IsFullWidth(CP_ACP,*pwPrevChar))) { // // If we are looking for the start of the word right, then we // stop when we have found it. (needed in case charLocated is // still FALSE) // break; } if (Edit_IsFullWidth(CP_ACP,*pwPrevChar)) { // // Previous character is a double width character. // // if we are in a word ( charLocated == TRUE ) // current position is the beginning of the word // if we are not in a word ( charLocated == FALSE ) // the previous character is what we looking for. // if ( !charLocated ) { pwWordMinSel = pwPrevChar; } break; } pwWordMinSel = pwPrevChar; if (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0A) { // // We have found the last char in the word. Continue looking // backwards till we find the first char of the word // charLocated = TRUE; // // We will consider a CR the start of a word // if (*pwWordMinSel == 0x0D) { break; } } } } else { // // We are moving right and we are in between words so we need to move // right till we find the start of a word (either a CR or a character. // while ((ISDELIMETERW(*pwWordMinSel) || *pwWordMinSel == 0x0A) && pwWordMinSel < pwText + ped->cch) { pwWordMinSel++; } } pwWordMaxSel = min((pwWordMinSel + 1), (pwText + ped->cch)); // // If pwWordMinSel points a double width character AND // pwWordMaxSel points non space then // pwWordMaxSel points the beggining of next word. // if (Edit_IsFullWidth(CP_ACP,*pwWordMinSel) && ! ISDELIMETERW(*pwWordMaxSel)) { goto FastReturnW; } if (*pwWordMinSel == 0x0D) { if (pwWordMinSel > pwText && *(pwWordMinSel - 1) == 0x0D) { // // So that we can treat CRCRLF as one word also. // pwWordMinSel--; } else if (*(pwWordMinSel + 1) == 0x0D) { // // Move MaxSel on to the LF // pwWordMaxSel++; } } // // Check if we have a one character word // if (ISDELIMETERW(*pwWordMaxSel)) { spaceLocated = TRUE; } // // Move pwWordMaxSel to the right looking for the end of a word and its // trailing spaces. WordMaxSel stops on the first character of the next // word. Thus, we break either at a CR or at the first nonspace char after // a run of spaces or LFs. // while ((pwWordMaxSel < pwText + ped->cch) && (!spaceLocated || (ISDELIMETERW(*pwWordMaxSel)))) { if (*pwWordMaxSel == 0x0D) { break; } // // treat double byte character as a word ( in unicode pwWordMaxSel loop ) // if it's a double width character // then we are at the beginning of // the next word which is a double // width character. // if (Edit_IsFullWidth(CP_ACP,*pwWordMaxSel)) { break; } pwWordMaxSel++; if (ISDELIMETERW(*pwWordMaxSel)) { spaceLocated = TRUE; } if (*(pwWordMaxSel - 1) == 0x0A) { break; } } // // label for fast return ( for Unicode ) // FastReturnW: Edit_Unlock(ped); if (pichMin) { *pichMin = (ICH)(pwWordMinSel - pwText); } if (pichMax) { *pichMax = (ICH)(pwWordMaxSel - pwText); } } } //---------------------------------------------------------------------------// // // Edit_SaveUndo() // // Saves old undo information into given buffer, and clears out info in // passed in undo buffer. If we're restoring, pundoFrom and pundoTo are // reversed. // VOID Edit_SaveUndo(PUNDO pundoFrom, PUNDO pundoTo, BOOL fClear) { // // Save undo data // RtlCopyMemory(pundoTo, pundoFrom, sizeof(UNDO)); // // Clear passed in undo buffer // if (fClear) { RtlZeroMemory(pundoFrom, sizeof(UNDO)); } } //---------------------------------------------------------------------------// // // Edit_EmptyUndo AorW // // Empties the undo buffer. // VOID Edit_EmptyUndo(PUNDO pundo) { if (pundo->hDeletedText) { GlobalFree(pundo->hDeletedText); } RtlZeroMemory(pundo, sizeof(UNDO)); } //---------------------------------------------------------------------------// // // Edit_MergeUndoInsertInfo() - // // When an insert takes place, this function is called with the info about // the new insertion (the insertion point and the count of chars inserted); // This looks at the existing Undo info and merges the new new insert info // with it. // VOID Edit_MergeUndoInsertInfo(PUNDO pundo, ICH ichInsert, ICH cchInsert) { // // If undo buffer is empty, just insert the new info as UNDO_INSERT // if (pundo->undoType == UNDO_NONE) { pundo->undoType = UNDO_INSERT; pundo->ichInsStart = ichInsert; pundo->ichInsEnd = ichInsert+cchInsert; } else if (pundo->undoType & UNDO_INSERT) { // // If there's already some undo insert info, // try to merge the two. // // // Check they are adjacent. // if (pundo->ichInsEnd == ichInsert) { // // if so, just concatenate. // pundo->ichInsEnd += cchInsert; } else { // // The new insert is not contiguous with the old one. // UNDOINSERT: // // If there is some UNDO_DELETE info already here, check to see // if the new insert takes place at a point different from where // that deletion occurred. // if ((pundo->undoType & UNDO_DELETE) && (pundo->ichDeleted != ichInsert)) { // // User is inserting into a different point; So, let us // forget any UNDO_DELETE info; // if (pundo->hDeletedText) { GlobalFree(pundo->hDeletedText); } pundo->hDeletedText = NULL; pundo->ichDeleted = 0xFFFFFFFF; pundo->undoType &= ~UNDO_DELETE; } // // Since the old insert and new insert are not adjacent, let us // forget everything about the old insert and keep just the new // insert info as the UNDO_INSERT. // pundo->ichInsStart = ichInsert; pundo->ichInsEnd = ichInsert + cchInsert; pundo->undoType |= UNDO_INSERT; } } else if (pundo->undoType == UNDO_DELETE) { // // If there is some Delete Info already present go and handle it. // goto UNDOINSERT; } } //---------------------------------------------------------------------------// // // Edit_InsertText AorW // // Adds cch characters from lpText into the ped->hText starting at // ped->ichCaret. Returns TRUE if successful else FALSE. Updates // ped->cchAlloc and ped->cch properly if additional memory was allocated or // if characters were actually added. Updates ped->ichCaret to be at the end // of the inserted text. min and maxsel are equal to ichcaret. // BOOL Edit_InsertText(PED ped, LPSTR lpText, ICH* pcchInsert) { PSTR pedText; PSTR pTextBuff; LONG style; HANDLE hTextCopy; DWORD allocamt; // // If the last byte (lpText[cchInsert - 1]) is a DBCS leading byte // we need to adjust it. // *pcchInsert = Edit_AdjustIch(ped, lpText, *pcchInsert); if (!*pcchInsert) { return TRUE; } // // Do we already have enough memory?? // if (*pcchInsert >= (ped->cchAlloc - ped->cch)) { // // Allocate what we need plus a little extra. Return FALSE if we are // unsuccessful. // allocamt = (ped->cch + *pcchInsert) * ped->cbChar; allocamt += CCHALLOCEXTRA; // if (!ped->fSingle) // { hTextCopy = LocalReAlloc(ped->hText, allocamt, LHND); if (hTextCopy) { ped->hText = hTextCopy; } else { return FALSE; } // } // else // { // if (!LocalReallocSafe(ped->hText, allocamt, LHND, pped)) // return FALSE; // } ped->cchAlloc = (ICH) LocalSize(ped->hText) / ped->cbChar; } // // Ok, we got the memory. Now copy the text into the structure // pedText = Edit_Lock(ped); if (ped->pLpkEditCallout) { HDC hdc; INT iResult; hdc = Edit_GetDC(ped, TRUE); iResult = ped->pLpkEditCallout->EditVerifyText((PED0)ped, hdc, pedText, ped->ichCaret, lpText, *pcchInsert); Edit_ReleaseDC (ped, hdc, TRUE); if (iResult == 0) { Edit_Unlock (ped); return TRUE; } } // // Get a pointer to the place where text is to be inserted // pTextBuff = pedText + ped->ichCaret * ped->cbChar; if (ped->ichCaret != ped->cch) { // // We are inserting text into the middle. We have to shift text to the // right before inserting new text. // memmove(pTextBuff + *pcchInsert * ped->cbChar, pTextBuff, (ped->cch-ped->ichCaret) * ped->cbChar); } // // Make a copy of the text being inserted in the edit buffer. // Use this copy for doing UPPERCASE/LOWERCASE ANSI/OEM conversions // Fix for Bug #3406 -- 01/29/91 -- SANKAR -- // memmove(pTextBuff, lpText, *pcchInsert * ped->cbChar); ped->cch += *pcchInsert; // // Get the control's style // style = GET_STYLE(ped); // // Do the Upper/Lower conversion // if (style & ES_LOWERCASE) { if (ped->fAnsi) { CharLowerBuffA((LPSTR)pTextBuff, *pcchInsert); } else { CharLowerBuffW((LPWSTR)pTextBuff, *pcchInsert); } } else { if (style & ES_UPPERCASE) { if (ped->fAnsi) { CharUpperBuffA(pTextBuff, *pcchInsert); } else { CharUpperBuffW((LPWSTR)pTextBuff, *pcchInsert); } } } // // Do the OEM conversion // // For backward compatibility with NT4, we don't perform OEM conversion // for older apps if the system locale is FarEast. // if ((style & ES_OEMCONVERT) && (!g_fDBCSEnabled || Is500Compat(UserGetVersion()) || GetOEMCP() != GetACP())) { ICH i; if (ped->fAnsi) { for (i = 0; i < *pcchInsert; i++) { // // We don't need to call CharToOemBuff etc. if the character // is a double byte character. And, calling Edit_IsDBCSLeadByte is // faster and less complicated because we don't have to deal // with the 2 byte dbcs cases. // if (g_fDBCSEnabled && Edit_IsDBCSLeadByte(ped, *(lpText+i))) { i++; continue; } if (IsCharLowerA(*(pTextBuff + i))) { CharUpperBuffA(pTextBuff + i, 1); CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1); OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1); CharLowerBuffA(pTextBuff + i, 1); } else { CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1); OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1); } } } else { // // Because 'ch' may become DBCS, and have a space for NULL. // UCHAR ch[4]; LPWSTR lpTextW = (LPWSTR)pTextBuff; for (i = 0; i < *pcchInsert; i++) { if (*(lpTextW + i) == UNICODE_CARRIAGERETURN || *(lpTextW + i) == UNICODE_LINEFEED || *(lpTextW + i) == UNICODE_TAB) { continue; } if (IsCharLowerW(*(lpTextW + i))) { CharUpperBuffW(lpTextW + i, 1); // // make sure the null-terminate. // *(LPDWORD)ch = 0; CharToOemBuffW(lpTextW + i, ch, 1); // // We assume any SBCS/DBCS character will converted // to 1 Unicode char, Otherwise, we may overwrite // next character... // OemToCharBuffW(ch, lpTextW + i, strlen(ch)); CharLowerBuffW(lpTextW + i, 1); } else { // // make sure the null-terminate. // *(LPDWORD)ch = 0; CharToOemBuffW(lpTextW + i, ch, 1); // // We assume any SBCS/DBCS character will converted // to 1 Unicode char, Otherwise, we may overwrite // next character... // OemToCharBuffW(ch, lpTextW + i, strlen(ch)); } } } } // // Adjust UNDO fields so that we can undo this insert... // Edit_MergeUndoInsertInfo(Pundo(ped), ped->ichCaret, *pcchInsert); ped->ichCaret += *pcchInsert; if (ped->pLpkEditCallout) { HDC hdc; hdc = Edit_GetDC(ped, TRUE); ped->ichCaret = ped->pLpkEditCallout->EditAdjustCaret((PED0)ped, hdc, pedText, ped->ichCaret); Edit_ReleaseDC (ped, hdc, TRUE); } ped->ichMinSel = ped->ichMaxSel = ped->ichCaret; Edit_Unlock(ped); // // Set dirty bit // ped->fDirty = TRUE; return TRUE; } //---------------------------------------------------------------------------// // // Edit_DeleteText AorW // // Deletes the text between ped->ichMinSel and ped->ichMaxSel. The // character at ichMaxSel is not deleted. But the character at ichMinSel is // deleted. ped->cch is updated properly and memory is deallocated if enough // text is removed. ped->ichMinSel, ped->ichMaxSel, and ped->ichCaret are set // to point to the original ped->ichMinSel. Returns the number of characters // deleted. // ICH Edit_DeleteText(PED ped) { PSTR pedText; ICH cchDelete; LPSTR lpDeleteSaveBuffer; HANDLE hDeletedText; DWORD bufferOffset; cchDelete = ped->ichMaxSel - ped->ichMinSel; if (cchDelete) { // // Ok, now lets delete the text. // pedText = Edit_Lock(ped); // // Adjust UNDO fields so that we can undo this delete... // if (ped->undoType == UNDO_NONE) { UNDODELETEFROMSCRATCH: if (ped->hDeletedText = GlobalAlloc(GPTR, (LONG)((cchDelete+1)*ped->cbChar))) { ped->undoType = UNDO_DELETE; ped->ichDeleted = ped->ichMinSel; ped->cchDeleted = cchDelete; lpDeleteSaveBuffer = ped->hDeletedText; RtlCopyMemory(lpDeleteSaveBuffer, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar); lpDeleteSaveBuffer[cchDelete*ped->cbChar] = 0; } } else if (ped->undoType & UNDO_INSERT) { UNDODELETE: Edit_EmptyUndo(Pundo(ped)); ped->ichInsStart = ped->ichInsEnd = 0xFFFFFFFF; ped->ichDeleted = 0xFFFFFFFF; ped->cchDeleted = 0; goto UNDODELETEFROMSCRATCH; } else if (ped->undoType == UNDO_DELETE) { if (ped->ichDeleted == ped->ichMaxSel) { // // Copy deleted text to front of undo buffer // hDeletedText = GlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND); if (!hDeletedText) { goto UNDODELETE; } bufferOffset = 0; ped->ichDeleted = ped->ichMinSel; } else if (ped->ichDeleted == ped->ichMinSel) { // // Copy deleted text to end of undo buffer // hDeletedText = GlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND); if (!hDeletedText) { goto UNDODELETE; } bufferOffset = ped->cchDeleted*ped->cbChar; } else { // // Clear the current UNDO delete and add the new one since // the deletes aren't contiguous. // goto UNDODELETE; } ped->hDeletedText = hDeletedText; lpDeleteSaveBuffer = (LPSTR)hDeletedText; if (!bufferOffset) { // // Move text in delete buffer up so that we can insert the next // text at the head of the buffer. // RtlMoveMemory(lpDeleteSaveBuffer + cchDelete*ped->cbChar, lpDeleteSaveBuffer, ped->cchDeleted*ped->cbChar); } RtlCopyMemory(lpDeleteSaveBuffer + bufferOffset, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar); lpDeleteSaveBuffer[(ped->cchDeleted + cchDelete)*ped->cbChar] = 0; ped->cchDeleted += cchDelete; } if (ped->ichMaxSel != ped->cch) { // // We are deleting text from the middle of the buffer so we have to // shift text to the left. // RtlMoveMemory(pedText + ped->ichMinSel*ped->cbChar, pedText + ped->ichMaxSel*ped->cbChar, (ped->cch - ped->ichMaxSel)*ped->cbChar); } if (ped->cchAlloc - ped->cch > CCHALLOCEXTRA) { // // Free some memory since we deleted a lot // LocalReAlloc(ped->hText, (DWORD)(ped->cch + (CCHALLOCEXTRA / 2))*ped->cbChar, LHND); ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar; } ped->cch -= cchDelete; if (ped->pLpkEditCallout) { HDC hdc; hdc = Edit_GetDC(ped, TRUE); ped->ichMinSel = ped->pLpkEditCallout->EditAdjustCaret((PED0)ped, hdc, pedText, ped->ichMinSel); Edit_ReleaseDC(ped, hdc, TRUE); } ped->ichCaret = ped->ichMaxSel = ped->ichMinSel; Edit_Unlock(ped); // // Set dirty bit // ped->fDirty = TRUE; } return cchDelete; } //---------------------------------------------------------------------------// // // Edit_NotifyParent AorW // // Sends the notification code to the parent of the edit control // VOID Edit_NotifyParent(PED ped, INT notificationCode) { // // wParam is NotificationCode (hiword) and WindowID (loword) // lParam is HWND of control sending the message // Windows 95 checks for hwndParent != NULL before sending the message, but // this is surely rare, and SendMessage NULL hwnd does nowt anyway (IanJa) // SendMessage(ped->hwndParent, WM_COMMAND, (DWORD)MAKELONG(GetWindowID(ped->hwnd), notificationCode), (LPARAM)ped->hwnd); } //---------------------------------------------------------------------------// // // Edit_SetClip AorW // // Sets the clip rect for the hdc to the formatting rectangle intersected // with the client area. // VOID Edit_SetClip(PED ped, HDC hdc, BOOL fLeftMargin) { RECT rcClient; RECT rcClip; INT cxBorder; INT cyBorder; CopyRect(&rcClip, &ped->rcFmt); if (ped->pLpkEditCallout) { // // Complex script handling chooses whether to write margins later // rcClip.left -= ped->wLeftMargin; rcClip.right += ped->wRightMargin; } else { // // Should we consider the left margin? // if (fLeftMargin) { rcClip.left -= ped->wLeftMargin; } // // Should we consider the right margin? // if (ped->fWrap) { rcClip.right += ped->wRightMargin; } } // // Set clip rectangle to rectClient intersect rectClip // We must clip for single line edits also. -- B#1360 // GetClientRect(ped->hwnd, &rcClient); if (ped->fFlatBorder) { cxBorder = GetSystemMetrics(SM_CXBORDER); cyBorder = GetSystemMetrics(SM_CYBORDER); InflateRect(&rcClient, cxBorder, cyBorder); } IntersectRect(&rcClient, &rcClient, &rcClip); IntersectClipRect(hdc,rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); } //---------------------------------------------------------------------------// // // Edit_GetDC AorW // // Hides the caret, gets the DC for the edit control, and clips to // the rcFmt rectangle specified for the edit control and sets the proper // font. If fFastDC, just select the proper font but don't bother about clip // regions or hiding the caret. // HDC Edit_GetDC(PED ped, BOOL fFastDC) { HDC hdc; if (!fFastDC) { HideCaret(ped->hwnd); } hdc = GetDC(ped->hwnd); if (hdc != NULL) { Edit_SetClip(ped, hdc, (BOOL)(ped->xOffset == 0)); // // Select the proper font for this edit control's dc. // if (ped->hFont) { SelectObject(hdc, ped->hFont); } } return hdc; } //---------------------------------------------------------------------------// // // Edit_ReleaseDC AorW // // Releases the DC (hdc) for the edit control and shows the caret. // If fFastDC, just select the proper font but don't bother about showing the // caret. // VOID Edit_ReleaseDC(PED ped, HDC hdc, BOOL fFastDC) { // // Restoring font not necessary // ReleaseDC(ped->hwnd, hdc); if (!fFastDC) { ShowCaret(ped->hwnd); } } //---------------------------------------------------------------------------// // // Edit_ResetTextInfo() AorW // // Handles a global change to the text by resetting text offsets, emptying // the undo buffer, and rebuilding the lines // VOID Edit_ResetTextInfo(PED ped) { // // Reset caret, selections, scrolling, and dirty information. // ped->iCaretLine = ped->ichCaret = 0; ped->ichMinSel = ped->ichMaxSel = 0; ped->xOffset = ped->ichScreenStart = 0; ped->fDirty = FALSE; Edit_EmptyUndo(Pundo(ped)); if (ped->fSingle) { if (!ped->listboxHwnd) { Edit_NotifyParent(ped, EN_UPDATE); } } else { EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL); } if (IsWindowVisible(ped->hwnd)) { BOOL fErase; if (ped->fSingle) { fErase = FALSE; } else { fErase = ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines); } // // Always redraw whether or not the insert was successful. We might // have NULL text. Paint() will check the redraw flag for us. // Edit_InvalidateClient(ped, fErase); // // BACKWARD COMPAT HACK: RAID expects the text to have been updated, // so we have to do an UpdateWindow here. It moves an edit control // around with fRedraw == FALSE, so it'll never get the paint message // with the control in the right place. // if (!ped->fWin31Compat) { UpdateWindow(ped->hwnd); } } if (ped->fSingle && !ped->listboxHwnd) { Edit_NotifyParent(ped, EN_CHANGE); } NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } //---------------------------------------------------------------------------// // // Edit_SetEditText AorW // // Copies the null terminated text in lpstr to the ped. Notifies the // parent if there isn't enough memory. Sets the minsel, maxsel, and caret to // the beginning of the inserted text. Returns TRUE if successful else FALSE // if no memory (and notifies the parent). // BOOL Edit_SetEditText(PED ped, LPSTR lpstr) { ICH cchLength; ICH cchSave = ped->cch; ICH ichCaretSave = ped->ichCaret; HWND hwndSave = ped->hwnd; HANDLE hText; ped->cch = ped->ichCaret = 0; ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar; if (!lpstr) { hText = LocalReAlloc(ped->hText, CCHALLOCEXTRA*ped->cbChar, LHND); if (hText != NULL) { ped->hText = hText; } else { return FALSE; } } else { cchLength = (ped->fAnsi ? strlen((LPSTR)lpstr) : wcslen((LPWSTR)lpstr)); // // Add the text // if (cchLength && !Edit_InsertText(ped, lpstr, &cchLength)) { // // Restore original state and notify parent we ran out of memory. // ped->cch = cchSave; ped->ichCaret = ichCaretSave; Edit_NotifyParent(ped, EN_ERRSPACE); return FALSE; } } ped->cchAlloc = (ICH)LocalSize(ped->hText) / ped->cbChar; if (IsWindow(hwndSave)) { Edit_ResetTextInfo(ped); } return TRUE; } //---------------------------------------------------------------------------// // // Edit_InvalidateClient() // // Invalidates client of edit field. For old 3.x guys with borders, // we draw it ourself (compatibility). So we don't want to invalidate // the border or we'll get flicker. // VOID Edit_InvalidateClient(PED ped, BOOL fErase) { if (ped->fFlatBorder) { RECT rcT; INT cxBorder; INT cyBorder; GetClientRect(ped->hwnd, &rcT); cxBorder = GetSystemMetrics(SM_CXBORDER); cyBorder = GetSystemMetrics(SM_CYBORDER); InflateRect(&rcT, cxBorder, cyBorder); InvalidateRect(ped->hwnd, &rcT, fErase); } else { InvalidateRect(ped->hwnd, NULL, fErase); } } //---------------------------------------------------------------------------// // // Edit_Copy AorW // // Copies the text between ichMinSel and ichMaxSel to the clipboard. // Returns the number of characters copied. // ICH Edit_Copy(PED ped) { HANDLE hData; char *pchSel; char *lpchClip; ICH cbData; // // Don't allow copies from password style controls // if (ped->charPasswordChar) { Edit_ShowBalloonTipWrap(ped->hwnd, IDS_PASSWORDCUT_TITLE, IDS_PASSWORDCUT_MSG, TTI_ERROR); MessageBeep(0); return 0; } cbData = (ped->ichMaxSel - ped->ichMinSel) * ped->cbChar; if (!cbData) { return 0; } if (!OpenClipboard(ped->hwnd)) { return 0; } EmptyClipboard(); hData = GlobalAlloc(LHND, (LONG)(cbData + ped->cbChar)); if (!hData) { CloseClipboard(); return 0; } lpchClip = GlobalLock(hData); UserAssert(lpchClip); pchSel = Edit_Lock(ped); pchSel = pchSel + (ped->ichMinSel * ped->cbChar); RtlCopyMemory(lpchClip, pchSel, cbData); if (ped->fAnsi) { *(lpchClip + cbData) = 0; } else { *(LPWSTR)(lpchClip + cbData) = (WCHAR)0; } Edit_Unlock(ped); GlobalUnlock(hData); SetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT, hData); CloseClipboard(); return cbData; } //---------------------------------------------------------------------------// LRESULT Edit_TrackBalloonTip(PED ped) { if (ped->hwndBalloon) { DWORD dwPackedCoords; HDC hdc = Edit_GetDC(ped, TRUE); RECT rcWindow; POINT pt; int cxCharOffset = TESTFLAG(GET_EXSTYLE(ped), WS_EX_RTLREADING) ? -ped->aveCharWidth : ped->aveCharWidth; // // Get the caret position // if (ped->fSingle) { pt.x = EditSL_IchToLeftXPos(ped, hdc, ped->ichCaret) + cxCharOffset; pt.y = ped->rcFmt.bottom; } else { EditML_IchToXYPos(ped, hdc, ped->ichCaret, FALSE, &pt); pt.x += cxCharOffset; pt.y += ped->lineHeight; } // // Translate to window coords // GetWindowRect(ped->hwnd, &rcWindow); pt.x += rcWindow.left; pt.y += rcWindow.top; // // Position the tip stem at the caret position // dwPackedCoords = (DWORD) MAKELONG(pt.x, pt.y); SendMessage(ped->hwndBalloon, TTM_TRACKPOSITION, 0, (LPARAM) dwPackedCoords); Edit_ReleaseDC(ped, hdc, TRUE); return 1; } return 0; } //---------------------------------------------------------------------------// LRESULT CALLBACK Edit_BalloonTipParentSubclassProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData) { PED ped = (PED)dwRefData; switch (uMessage) { case WM_MOVE: case WM_SIZING: // // dismiss any showing tips // Edit_HideBalloonTip(ped->hwnd); break; case WM_DESTROY: // Clean up subclass RemoveWindowSubclass(hDlg, Edit_BalloonTipParentSubclassProc, (UINT_PTR) ped->hwnd); break; default: break; } return DefSubclassProc(hDlg, uMessage, wParam, lParam); } //---------------------------------------------------------------------------// LRESULT Edit_BalloonTipSubclassParents(PED ped) { // Subclass all windows along the parent chain from the edit control // and in the same thread (can only subclass windows with same thread affinity) HWND hwndParent = GetAncestor(ped->hwnd, GA_PARENT); DWORD dwTid = GetWindowThreadProcessId(ped->hwnd, NULL); while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL))) { SetWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR)ped->hwnd, (DWORD_PTR)ped); hwndParent = GetAncestor(hwndParent, GA_PARENT); } return TRUE; } //---------------------------------------------------------------------------// HWND Edit_BalloonTipRemoveSubclasses(PED ped) { HWND hwndParent = GetAncestor(ped->hwnd, GA_PARENT); HWND hwndTopMost = NULL; DWORD dwTid = GetWindowThreadProcessId(ped->hwnd, NULL); while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL))) { RemoveWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR) ped->hwnd); hwndTopMost = hwndParent; hwndParent = GetAncestor(hwndParent, GA_PARENT); } return hwndTopMost; } //---------------------------------------------------------------------------// LRESULT Edit_HideBalloonTipHandler(PED ped) { if (ped->hwndBalloon) { HWND hwndParent; KillTimer(ped->hwnd, ID_EDITTIMER); SendMessage(ped->hwndBalloon, TTM_TRACKACTIVATE, FALSE, 0); DestroyWindow(ped->hwndBalloon); ped->hwndBalloon = NULL; hwndParent = Edit_BalloonTipRemoveSubclasses(ped); if (hwndParent && IsWindow(hwndParent)) { InvalidateRect(hwndParent, NULL, TRUE); UpdateWindow(hwndParent); } if (hwndParent != ped->hwnd) { RedrawWindow(ped->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } } return TRUE; } //---------------------------------------------------------------------------// __inline LRESULT Edit_ShowBalloonTipWrap(HWND hwnd, DWORD dwTitleId, DWORD dwMsgId, DWORD dwIconId) { WCHAR szTitle[56]; WCHAR szMsg[MAX_PATH]; EDITBALLOONTIP ebt; LoadString(HINST_THISDLL, dwTitleId, szTitle, ARRAYSIZE(szTitle)); LoadString(HINST_THISDLL, dwMsgId, szMsg, ARRAYSIZE(szMsg)); ebt.cbStruct = sizeof(ebt); ebt.pszTitle = szTitle; ebt.pszText = szMsg; ebt.ttiIcon = dwIconId; return Edit_ShowBalloonTip(hwnd, &ebt); } //---------------------------------------------------------------------------// LRESULT Edit_ShowBalloonTipHandler(PED ped, PEDITBALLOONTIP pebt) { LRESULT lResult = FALSE; Edit_HideBalloonTipHandler(ped); if (sizeof(EDITBALLOONTIP) == pebt->cbStruct) { ped->hwndBalloon = CreateWindowEx( (IS_BIDI_LOCALIZED_SYSTEM() ? WS_EX_LAYOUTRTL : 0), TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, ped->hwnd, NULL, g_hinst, NULL); if (NULL != ped->hwndBalloon) { TOOLINFO ti = {0}; ti.cbSize = TTTOOLINFOW_V2_SIZE; ti.uFlags = TTF_IDISHWND | TTF_TRACK; ti.hwnd = ped->hwnd; ti.uId = (WPARAM)1; ti.lpszText = (LPWSTR)pebt->pszText; SendMessage(ped->hwndBalloon, TTM_ADDTOOL, 0, (LPARAM)&ti); SendMessage(ped->hwndBalloon, TTM_SETMAXTIPWIDTH, 0, 300); SendMessage(ped->hwndBalloon, TTM_SETTITLE, (WPARAM) pebt->ttiIcon, (LPARAM)pebt->pszTitle); Edit_TrackBalloonTip(ped); SendMessage(ped->hwndBalloon, TTM_TRACKACTIVATE, (WPARAM) TRUE, (LPARAM)&ti); SetFocus(ped->hwnd); Edit_BalloonTipSubclassParents(ped); // // set timeout to kill the tip // KillTimer(ped->hwnd, ID_EDITTIMER); SetTimer(ped->hwnd, ID_EDITTIMER, EDIT_TIPTIMEOUT, NULL); lResult = TRUE; } } return lResult; } //---------------------------------------------------------------------------// BOOL Edit_ClientEdgePaint(PED ped, HRGN hRgnUpdate) { HDC hdc; BOOL bRet = FALSE; hdc = (hRgnUpdate != NULL) ? GetDCEx(ped->hwnd, hRgnUpdate, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE | DCX_INTERSECTRGN | DCX_NODELETERGN) : GetDCEx(ped->hwnd, NULL, DCX_USESTYLE | DCX_WINDOW | DCX_LOCKWINDOWUPDATE); if (hdc) { HBRUSH hbr; BOOL fDeleteBrush = FALSE; hbr = Edit_GetBrush(ped, hdc, &fDeleteBrush); if (hbr) { RECT rc; HRGN hrgn; INT iStateId = Edit_GetStateId(ped); INT cxBorder = 0, cyBorder = 0; if (SUCCEEDED(GetThemeInt(ped->hTheme, EP_EDITTEXT, iStateId, TMT_SIZINGBORDERWIDTH, &cxBorder))) { cyBorder = cxBorder; } else { cxBorder = g_cxBorder; cyBorder = g_cyBorder; } GetWindowRect(ped->hwnd, &rc); // // Create an update region without the client edge // to pass to DefWindowProc // InflateRect(&rc, -g_cxEdge, -g_cyEdge); hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); if (hRgnUpdate != NULL) { CombineRgn(hrgn, hRgnUpdate, hrgn, RGN_AND); } // // Zero-origin the rect // OffsetRect(&rc, -rc.left, -rc.top); // // clip our drawing to the non-client edge // OffsetRect(&rc, g_cxEdge, g_cyEdge); ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); InflateRect(&rc, g_cxEdge, g_cyEdge); DrawThemeBackground(ped->hTheme, hdc, EP_EDITTEXT, iStateId, &rc, 0); // // Fill with the control's brush first since the ThemeBackground // border may not be as thick as the client edge // if ((cxBorder < g_cxEdge) && (cyBorder < g_cyEdge)) { InflateRect(&rc, cxBorder-g_cxEdge, cyBorder-g_cyEdge); FillRect(hdc, &rc, hbr); } DefWindowProc(ped->hwnd, WM_NCPAINT, (WPARAM)hrgn, 0); DeleteObject(hrgn); if (fDeleteBrush) { DeleteObject(hbr); } bRet = TRUE; } ReleaseDC(ped->hwnd, hdc); } return bRet; } //---------------------------------------------------------------------------// // // Edit_WndProc // // WndProc for all edit controls. // Dispatches all messages to the appropriate handlers which are named // as follows: // EditSL_ (single line) prefixes all single line edit control // EditML_ (multi line) prefixes all multi- line edit controls // Edit_ (edit control) prefixes all common handlers // // The Edit_WndProc only handles messages common to both single and multi // line edit controls. Messages which are handled differently between // single and multi are sent to EditSL_WndProc or EditML_WndProc. // LRESULT Edit_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PED ped; LRESULT lResult; // // Get the instance data for this edit control // ped = Edit_GetPtr(hwnd); if (!ped && uMsg != WM_NCCREATE) { return DefWindowProc(hwnd, uMsg, wParam, lParam); } // // Dispatch the various messages we can receive // lResult = 1L; switch (uMsg) { // // Messages which are handled the same way for both single and multi line // edit controls. // case WM_KEYDOWN: // // LPK handling of Ctrl/LShift, Ctrl/RShift // if (ped && ped->pLpkEditCallout && ped->fAllowRTL) { // // Any keydown cancels a ctrl/shift reading order change // ped->fSwapRoOnUp = FALSE; switch (wParam) { case VK_SHIFT: if ((GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) { // // Left shift or right shift pressed while control held down // Check that alt (VK_MENU) isn't down to avoid false firing // on AltGr which equals Ctrl+Alt. // if (MapVirtualKey((LONG)lParam>>16&0xff, 3) == VK_LSHIFT) { // // User wants left to right reading order // ped->fSwapRoOnUp = (ped->fRtoLReading) || (ped->format & ES_RIGHT); ped->fLShift = TRUE; } else { // // User wants right to left reading order // ped->fSwapRoOnUp = (!ped->fRtoLReading) || (ped->format & ES_RIGHT); ped->fLShift = FALSE; } } break; case VK_LEFT: if (ped->fRtoLReading) { wParam = VK_RIGHT; } break; case VK_RIGHT: if (ped->fRtoLReading) { wParam = VK_LEFT; } break; } } goto HandleEditMsg; case WM_KEYUP: if (ped && ped->pLpkEditCallout && ped->fAllowRTL && ped->fSwapRoOnUp) { BOOL fReadingOrder; // // Complete reading order change detected earlier during keydown // ped->fSwapRoOnUp = FALSE; fReadingOrder = ped->fRtoLReading; // // Remove any overriding ES_CENTRE or ES_RIGHT format from dwStyle // SetWindowLong(hwnd, GWL_STYLE, (GET_STYLE(ped) & ~ES_FMTMASK)); if (ped->fLShift) { // // Set Left to Right reading order and right scrollbar in EX_STYLE // SetWindowLong(hwnd, GWL_EXSTYLE, (GET_EXSTYLE(ped) & ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR))); // // Edit control is LTR now, then notify the parent. // Edit_NotifyParent(ped, EN_ALIGN_LTR_EC); // // ? Select a keyboard layout appropriate to LTR operation // } else { // // Set Right to Left reading order, right alignment and left scrollbar // SetWindowLong(hwnd, GWL_EXSTYLE, GET_EXSTYLE(ped) | WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR); // // Edit control is RTL now, then notify the parent. // Edit_NotifyParent(ped, EN_ALIGN_RTL_EC); // // ? Select a keyboard layout appropriate to RTL operation // } // // If reading order didn't change, so we are sure the alignment // changed and the edit window didn't invalidate yet. // if (fReadingOrder == (BOOL) ped->fRtoLReading) { Edit_InvalidateClient(ped, TRUE); } } goto HandleEditMsg; case WM_INPUTLANGCHANGE: if (ped) { // // EC_INSERT_COMPOSITION_CHAR : WM_INPUTLANGCHANGE - call Edit_InitInsert() // HKL hkl = GetKeyboardLayout(0); Edit_InitInsert(ped, hkl); if (ped->fInReconversion) { Edit_InOutReconversionMode(ped, FALSE); } // // Font and caret position might be changed while // another keyboard layout is active. Set those // if the edit control has the focus. // if (ped->fFocus && ImmIsIME(hkl)) { POINT pt; Edit_SetCompositionFont(ped); GetCaretPos(&pt); Edit_ImmSetCompositionWindow(ped, pt.x, pt.y); } } goto HandleEditMsg; case WM_COPY: // // wParam - not used // lParam - not used // lResult = (LONG)Edit_Copy(ped); break; case WM_CUT: // // wParam -- not used // lParam -- not used // Edit_CutText(ped); lResult = 0; break; case WM_CLEAR: // // wParam - not used // lParam - not used // Edit_ClearText(ped); lResult = 0; break; case WM_ENABLE: // // wParam - nonzero if window is enabled else disable window if 0. // lParam - not used // ped->fDisabled = !((BOOL)wParam); CCInvalidateFrame(hwnd); Edit_InvalidateClient(ped, TRUE); lResult = (LONG)ped->fDisabled; break; case WM_SYSCHAR: // // wParam - key value // lParam - not used // // // If this is a WM_SYSCHAR message generated by the UNDO // keystroke we want to EAT IT // if ((lParam & SYS_ALTERNATE) && ((WORD)wParam == VK_BACK)) { lResult = TRUE; } else { lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); } break; case EM_GETLINECOUNT: // // wParam - not used // lParam - not used // lResult = (LONG)ped->cLines; break; case EM_GETMODIFY: // // wParam - not used // lParam - not used // // // Gets the state of the modify flag for this edit control. // lResult = (LONG)ped->fDirty; break; case EM_SETMODIFY: // // wParam - specifies the new value for the modify flag // lParam - not used // // // Sets the state of the modify flag for // this edit control. // ped->fDirty = (wParam != 0); break; case EM_GETRECT: // // wParam - not used // lParam - pointer to a RECT data structure that gets the dimensions. // // // Copies the rcFmt rect to *lpRect. // CopyRect((LPRECT)lParam, (LPRECT)&ped->rcFmt); lResult = (LONG)TRUE; break; case WM_GETFONT: // // wParam - not used // lParam - not used // lResult = (LRESULT)ped->hFont; break; case WM_SETFONT: // // wParam - handle to the font // lParam - redraw if true else don't // Edit_SetFont(ped, (HANDLE)wParam, (BOOL)LOWORD(lParam)); break; case WM_GETTEXT: // // wParam - max number of _bytes_ (not characters) to copy // lParam - buffer to copy text to. Text is 0 terminated. // lResult = (LRESULT)Edit_GetTextHandler(ped, (ICH)wParam, (LPSTR)lParam, TRUE); break; case WM_SETTEXT: // // wParam - not used // lParam - LPSTR, null-terminated, with new text. // lResult = (LRESULT)Edit_SetEditText(ped, (LPSTR)lParam); break; case WM_GETTEXTLENGTH: // // Return count of CHARs!!! // lResult = (LONG)ped->cch; break; case WM_DESTROY: // // Make sure we unsubclass for the balloon tip, if appropriate // lResult = Edit_HideBalloonTipHandler(ped); break; case WM_NCDESTROY: case WM_FINALDESTROY: // // wParam - not used // lParam - not used // Edit_NcDestroyHandler(hwnd, ped); lResult = 0; break; case WM_RBUTTONDOWN: // // Most apps (i.e. everyone but Quicken) don't pass on the rbutton // messages when they do something with 'em inside of subclassed // edit fields. As such, we keep track of whether we saw the // down before the up. If we don't see the up, then DefWindowProc // won't generate the context menu message, so no big deal. If // we didn't see the down, then don't let WM_CONTEXTMENU do // anything. // // We also might want to not generate WM_CONTEXTMENUs for old // apps when the mouse is captured. // ped->fSawRButtonDown = TRUE; goto HandleEditMsg; case WM_RBUTTONUP: if (ped->fSawRButtonDown) { ped->fSawRButtonDown = FALSE; if (!ped->fInReconversion) { goto HandleEditMsg; } } // // Don't pass this on to DWP so WM_CONTEXTMENU isn't generated. // lResult = 0; break; case WM_CONTEXTMENU: { POINT pt; INT nHit = (INT)DefWindowProc(hwnd, WM_NCHITTEST, 0, lParam); if ((nHit == HTVSCROLL) || (nHit == HTHSCROLL)) { lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); } else { POINTSTOPOINT(pt, lParam); if (!TESTFLAG(GET_STATE2(ped), WS_S2_OLDUI) && Edit_IsAncestorActive(hwnd)) { Edit_Menu(hwnd, ped, &pt); } lResult = 0; } break; } case EM_CANUNDO: // // wParam - not used // lParam - not used // lResult = (LONG)(ped->undoType != UNDO_NONE); break; case EM_EMPTYUNDOBUFFER: // // wParam - not used // lParam - not used // Edit_EmptyUndo(Pundo(ped)); break; case EM_GETMARGINS: // // wParam - not used // lParam - not used // lResult = MAKELONG(ped->wLeftMargin, ped->wRightMargin); break; case EM_SETMARGINS: // // wParam - EC_ margin flags // lParam - LOWORD is left, HIWORD is right margin // Edit_SetMargin(ped, (UINT)wParam, (DWORD)lParam, TRUE); lResult = 0; break; case EM_GETSEL: // // Gets the selection range for the given edit control. The // starting position is in the low order word. It contains the position // of the first nonselected character after the end of the selection in // the high order word. // if ((PDWORD)wParam != NULL) { *((PDWORD)wParam) = ped->ichMinSel; } if ((PDWORD)lParam != NULL) { *((PDWORD)lParam) = ped->ichMaxSel; } lResult = MAKELONG(ped->ichMinSel,ped->ichMaxSel); break; case EM_GETLIMITTEXT: // // wParam - not used // lParam - not used // lResult = ped->cchTextMax; break; case EM_SETLIMITTEXT: // // wParam - max number of CHARACTERS that can be entered // lParam - not used // // // Specifies the maximum number of characters of text the user may // enter. If maxLength is 0, we may enter MAXINT number of CHARACTERS. // if (ped->fSingle) { if (wParam) { wParam = min(0x7FFFFFFEu, wParam); } else { wParam = 0x7FFFFFFEu; } } if (wParam) { ped->cchTextMax = (ICH)wParam; } else { ped->cchTextMax = 0xFFFFFFFFu; } break; case EM_POSFROMCHAR: // // Validate that char index is within text range // if (wParam >= ped->cch) { lResult = -1L; } else { goto HandleEditMsg; } break; case EM_CHARFROMPOS: { // // Validate that point is within client of edit field // RECT rc; POINT pt; POINTSTOPOINT(pt, lParam); GetClientRect(hwnd, &rc); if (!PtInRect(&rc, pt)) { lResult = -1L; } else { goto HandleEditMsg; } break; } case EM_SETPASSWORDCHAR: // // wParam - sepecifies the new char to display instead of the // real text. if null, display the real text. // Edit_SetPasswordCharHandler(ped, (UINT)wParam); break; case EM_GETPASSWORDCHAR: lResult = (DWORD)ped->charPasswordChar; break; case EM_SETREADONLY: // // wParam - state to set read only flag to // ped->fReadOnly = (wParam != 0); if (wParam) { SetWindowState(hwnd, ES_READONLY); } else { ClearWindowState(hwnd, ES_READONLY); } lResult = 1L; if ( g_fIMMEnabled ) { Edit_EnableDisableIME( ped ); } // // We need to redraw the edit field so that the background color // changes. Read-only edits are drawn in CTLCOLOR_STATIC while // others are drawn with CTLCOLOR_EDIT. // Edit_InvalidateClient(ped, TRUE); break; case EM_SETWORDBREAKPROC: // wParam - not used // lParam - PROC address of an app supplied call back function ped->lpfnNextWord = (EDITWORDBREAKPROCA)lParam; break; case EM_GETWORDBREAKPROC: lResult = (LRESULT)ped->lpfnNextWord; break; case EM_GETIMESTATUS: // // wParam == sub command // if (wParam == EMSIS_COMPOSITIONSTRING) { lResult = ped->wImeStatus; } break; case EM_SETIMESTATUS: // // wParam == sub command // if (wParam == EMSIS_COMPOSITIONSTRING) { ped->wImeStatus = (WORD)lParam; } break; case WM_NCCREATE: ped = (PED)UserLocalAlloc(HEAP_ZERO_MEMORY, sizeof(ED)); if (ped) { // // Success... store the instance pointer. // TraceMsg(TF_STANDARD, "EDIT: Setting edit instance pointer."); Edit_SetPtr(hwnd, ped); lResult = Edit_NcCreate(ped, hwnd, (LPCREATESTRUCT)lParam); } else { // // Failed... return FALSE. // // From a WM_NCCREATE msg, this will cause the // CreateWindow call to fail. // TraceMsg(TF_STANDARD, "EDIT: Unable to allocate edit instance structure."); lResult = FALSE; } break; case WM_LBUTTONDOWN: // // B#3623 // Don't set focus to edit field if it is within an inactive, // captioned child. // We might want to version switch this... I haven't found // any problems by not, but you never know... // if (Edit_IsAncestorActive(hwnd)) { // // Reconversion support: quit reconversion if left button is clicked. // Otherwise, if the current KL is Korean, finailize the composition string. // if (ped->fInReconversion || ped->fKorea) { BOOLEAN fReconversion = (BOOLEAN)ped->fInReconversion; DWORD dwIndex = fReconversion ? CPS_CANCEL : CPS_COMPLETE; HIMC hImc; ped->fReplaceCompChr = FALSE; hImc = ImmGetContext(ped->hwnd); if (hImc) { ImmNotifyIME(hImc, NI_COMPOSITIONSTR, dwIndex, 0); ImmReleaseContext(ped->hwnd, hImc); } if (fReconversion) { Edit_InOutReconversionMode(ped, FALSE); } Edit_SetCaretHandler(ped); } goto HandleEditMsg; } break; case WM_MOUSELEAVE: if (ped->hTheme && ped->fHot) { ped->fHot = FALSE; SendMessage(ped->hwnd, WM_NCPAINT, 1, 0); } break; case WM_MOUSEMOVE: // // If the hot bit is not already set // and we are themed // if (ped->hTheme && !ped->fHot) { TRACKMOUSEEVENT tme; // // Set the hot bit and request that // we be notified when the mouse leaves // ped->fHot = TRUE; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = ped->hwnd; tme.dwHoverTime = 0; TrackMouseEvent(&tme); SendMessage(ped->hwnd, WM_NCPAINT, 1, 0); } // // We only care about mouse messages when mouse is down. // if (ped->fMouseDown) { goto HandleEditMsg; } break; case WM_NCPAINT: // // Draw our own client edge border when themed // if (ped->hTheme && TESTFLAG(GET_EXSTYLE(ped), WS_EX_CLIENTEDGE)) { if (Edit_ClientEdgePaint(ped, ((wParam != 1) ? (HRGN)wParam : NULL))) { break; } } goto HandleEditMsg; case WM_WININICHANGE: InitGlobalMetrics(wParam); break; case WM_IME_SETCONTEXT: // // If ped->fInsertCompChr is TRUE, that means we will do // all the composition character drawing by ourself. // if (ped->fInsertCompChr ) { lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; } if (wParam) { PINPUTCONTEXT pInputContext; HIMC hImc; hImc = ImmGetContext(hwnd); pInputContext = ImmLockIMC(hImc); if (pInputContext != NULL) { pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS; ImmUnlockIMC( hImc ); } ImmReleaseContext( hwnd, hImc ); } lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); break; case WM_IME_ENDCOMPOSITION: Edit_InOutReconversionMode(ped, FALSE); if (ped->fReplaceCompChr) { ICH ich; HDC hdc; // // 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 (ped->fSingle) { if (Edit_DeleteText( ped ) > 0) { // // 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); } } else { EditML_DeleteText(ped); } Edit_SetCaretHandler( ped ); } lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); break; case WM_IME_STARTCOMPOSITION: if ( ped->fInsertCompChr ) { // // BUG BUG // // sending WM_IME_xxxCOMPOSITION will let // IME draw composition window. IME should // not do that since we cleared // ISC_SHOWUICOMPOSITIONWINDOW bit when // we got WM_IME_SETCONTEXT message. // // Korean IME should be fixed in the future. // break; } else { lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); } break; case WM_IME_COMPOSITION: // // simple composition character support for FE IME. // lResult = Edit_ImeComposition(ped, wParam, lParam); break; case WM_IME_NOTIFY: if (ped->fInReconversion && (wParam == IMN_GUIDELINE)) { HIMC hImc = ImmGetContext(hwnd); if ((hImc != NULL_HIMC) && (ImmGetGuideLine(hImc, GGL_LEVEL, NULL, 0) >= GL_LEVEL_WARNING)) { // #266916 Restore the cursor if conversion failed. Conversion can fail // if you try converting 100+ chars at once. Edit_InOutReconversionMode(ped, FALSE); } } lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); break; case WM_KILLFOCUS: // // remove any tips // if (ped->hwndBalloon) { BOOL fClickedTip = (ped->hwndBalloon == (HWND)wParam) ? TRUE : FALSE; Edit_HideBalloonTip(ped->hwnd); if (fClickedTip) { // // Don't remove focus from the edit because they // clicked on the tip. // SetFocus(hwnd); break; } } // // when focus is removed from the window, // composition character should be finalized // if (ped && g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0))) { HIMC hImc = ImmGetContext(hwnd); if (hImc != NULL_HIMC) { if (ped->fReplaceCompChr || (ped->wImeStatus & EIMES_COMPLETECOMPSTRKILLFOCUS)) { // // If the composition string to be determined upon kill focus, // do it now. // ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); } else if (ped->fInReconversion) { // // If the composition string it not to be determined, // and if we're in reconversion mode, cancel reconversion now. // ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); } // // Get out from reconversion mode // if (ped->fInReconversion) { Edit_InOutReconversionMode(ped, FALSE); } ImmReleaseContext(hwnd, hImc); } } goto HandleEditMsg; break; case WM_SETFOCUS: if (ped && !ped->fFocus) { HKL hkl = GetKeyboardLayout(0); if (g_fIMMEnabled && ImmIsIME(hkl)) { HIMC hImc; hImc = ImmGetContext(hwnd); if (hImc) { LPINPUTCONTEXT lpImc; if (ped->wImeStatus & EIMES_CANCELCOMPSTRINFOCUS) { // // cancel when in-focus // ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); } Edit_SetCompositionFont(ped); if ((lpImc = ImmLockIMC(hImc)) != NULL) { // // We presume the CompForm will reset to CFS_DEFAULT, // when the edit control loses Focus. // IMEWndProc32 will call ImmSetCompositionWindow with // CFS_DEFAULT, when it receive WM_IME_SETCONTEXT. // lpImc->fdw31Compat |= F31COMPAT_ECSETCFS; ImmUnlockIMC(hImc); } ImmReleaseContext(hwnd, hImc); } // // force to set IME composition window when // first getting focus. // ped->ptScreenBounding.x = -1; ped->ptScreenBounding.y = -1; } Edit_InitInsert(ped, hkl); } goto HandleEditMsg; break; case WM_IME_REQUEST: // // simple ImeRequest Handler // lResult = Edit_RequestHandler(ped, wParam, lParam); break; case WM_CREATE: if (g_fIMMEnabled && ped) { Edit_EnableDisableIME(ped); } goto HandleEditMsg; break; case WM_GETOBJECT: if(lParam == OBJID_QUERYCLASSNAMEIDX) { lResult = MSAA_CLASSNAMEIDX_EDIT; } else { lResult = FALSE; } break; case WM_THEMECHANGED: if ( ped->hTheme ) { CloseThemeData(ped->hTheme); } ped->hTheme = OpenThemeData(ped->hwnd, L"Edit"); if ( ped->hFontSave ) { Edit_SetFont(ped, ped->hFontSave, FALSE); } InvalidateRect(ped->hwnd, NULL, TRUE); lResult = TRUE; break; case EM_SHOWBALLOONTIP: lResult = Edit_ShowBalloonTipHandler(ped, (PEDITBALLOONTIP) lParam); break; case EM_HIDEBALLOONTIP: lResult = Edit_HideBalloonTipHandler(ped); break; case WM_TIMER: if (wParam == ID_EDITTIMER) { KillTimer(ped->hwnd, ID_EDITTIMER); lResult = Edit_HideBalloonTip(ped->hwnd); } break; default: HandleEditMsg: if (ped != NULL) { if (ped->fSingle) { lResult = EditSL_WndProc(ped, uMsg, wParam, lParam); } else { lResult = EditML_WndProc(ped, uMsg, wParam, lParam); } } } return lResult; } //---------------------------------------------------------------------------// // // Edit_FindXORblks // // This finds the XOR of lpOldBlk and lpNewBlk and return s resulting blocks // through the lpBlk1 and lpBlk2; This could result in a single block or // at the maximum two blocks; // If a resulting block is empty, then it's StPos field has -1. // // NOTE: // When called from MultiLine edit control, StPos and EndPos fields of // these blocks have the Starting line and Ending line of the block; // When called from SingleLine edit control, StPos and EndPos fields // of these blocks have the character index of starting position and // ending position of the block. // VOID Edit_FindXORblks(LPSELBLOCK lpOldBlk, LPSELBLOCK lpNewBlk, LPSELBLOCK lpBlk1, LPSELBLOCK lpBlk2) { if (lpOldBlk->StPos >= lpNewBlk->StPos) { lpBlk1->StPos = lpNewBlk->StPos; lpBlk1->EndPos = min(lpOldBlk->StPos, lpNewBlk->EndPos); } else { lpBlk1->StPos = lpOldBlk->StPos; lpBlk1->EndPos = min(lpNewBlk->StPos, lpOldBlk->EndPos); } if (lpOldBlk->EndPos <= lpNewBlk->EndPos) { lpBlk2->StPos = max(lpOldBlk->EndPos, lpNewBlk->StPos); lpBlk2->EndPos = lpNewBlk->EndPos; } else { lpBlk2->StPos = max(lpNewBlk->EndPos, lpOldBlk->StPos); lpBlk2->EndPos = lpOldBlk->EndPos; } } //---------------------------------------------------------------------------// // BOOL Edit_CalcChangeSelection(PED ped, ICH ichOldMinSel, ICH ichOldMaxSel, LPSELBLOCK OldBlk, LPSELBLOCK NewBlk) { SELBLOCK Blk[2]; int iBlkCount = 0; Blk[0].StPos = Blk[0].EndPos = Blk[1].StPos = Blk[1].EndPos = 0xFFFFFFFF; // // Check if the Old selection block existed // if (ichOldMinSel != ichOldMaxSel) { // // Yes! Old block existed. // Blk[0].StPos = OldBlk->StPos; Blk[0].EndPos = OldBlk->EndPos; iBlkCount++; } // // Check if the new Selection block exists // if (ped->ichMinSel != ped->ichMaxSel) { // // Yes! New block exists // Blk[1].StPos = NewBlk->StPos; Blk[1].EndPos = NewBlk->EndPos; iBlkCount++; } // // If both the blocks exist find the XOR of them // if (iBlkCount == 2) { // // Check if both blocks start at the same character position // if (ichOldMinSel == ped->ichMinSel) { // // Check if they end at the same character position // if (ichOldMaxSel == ped->ichMaxSel) { // // Nothing changes // return FALSE; } Blk[0].StPos = min(NewBlk -> EndPos, OldBlk -> EndPos); Blk[0].EndPos = max(NewBlk -> EndPos, OldBlk -> EndPos); Blk[1].StPos = 0xFFFFFFFF; } else { if (ichOldMaxSel == ped->ichMaxSel) { Blk[0].StPos = min(NewBlk->StPos, OldBlk->StPos); Blk[0].EndPos = max(NewBlk->StPos, OldBlk->StPos); Blk[1].StPos = 0xFFFFFFFF; } else { Edit_FindXORblks(OldBlk, NewBlk, &Blk[0], &Blk[1]); } } } RtlCopyMemory(OldBlk, &Blk[0], sizeof(SELBLOCK)); RtlCopyMemory(NewBlk, &Blk[1], sizeof(SELBLOCK)); return TRUE; } //---------------------------------------------------------------------------// // // Edit_GetControlBrush // // Client side optimization replacement for NtUserGetControlBrush // message is one of the WM_CTLCOLOR* messages. // HBRUSH Edit_GetControlBrush(PED ped, HDC hdc, LONG message) { HWND hwndSend; hwndSend = (GET_STYLE(ped) & WS_POPUP) ? GetWindowOwner(ped->hwnd) : GetParent(ped->hwnd); if (!hwndSend) { hwndSend = ped->hwnd; } // // By using the correct A/W call we avoid a c/s transition // on this SendMessage(). // return (HBRUSH)SendMessage(hwndSend, message, (WPARAM)hdc, (LPARAM)ped->hwnd); } //---------------------------------------------------------------------------// // // Edit_GetDBCSVector // // This function sets DBCS Vector for specified character set and sets // ped->fDBCS flag if needed. // INT Edit_GetDBCSVector(PED ped, HDC hdc, BYTE CharSet) { BOOL bDBCSCodePage = FALSE; static UINT fFontAssocStatus = 0xffff; // // if DEFAUT_CHARSET was passed, we will convert that to Shell charset.. // if (CharSet == DEFAULT_CHARSET) { CharSet = (BYTE)GetTextCharset(hdc); // // if CharSet is still DEFAULT_CHARSET, it means gdi has some problem.. // then just return default.. we get charset from CP_ACP.. // if (CharSet == DEFAULT_CHARSET) { CharSet = (BYTE)GetACPCharSet(); } } switch (CharSet) { case SHIFTJIS_CHARSET: case HANGEUL_CHARSET: case CHINESEBIG5_CHARSET: case GB2312_CHARSET: bDBCSCodePage = TRUE; break; case ANSI_CHARSET: // 0 case SYMBOL_CHARSET: // 2 case OEM_CHARSET: // 255 if (fFontAssocStatus == 0xffff) { fFontAssocStatus = QueryFontAssocStatus(); } if ((((CharSet + 2) & 0xf) & fFontAssocStatus)) { bDBCSCodePage = TRUE; // // Bug 117558, etc. // Try to get a meaningful character set for associated font. // CharSet = (BYTE)GetACPCharSet(); } else { bDBCSCodePage = FALSE; } break; default: bDBCSCodePage = FALSE; } if (bDBCSCodePage) { CHARSETINFO CharsetInfo; DWORD CodePage; CPINFO CPInfo; INT lbIX; if (TranslateCharsetInfo((DWORD *)CharSet, &CharsetInfo, TCI_SRCCHARSET)) { CodePage = CharsetInfo.ciACP; } else { CodePage = CP_ACP; } GetCPInfo(CodePage, &CPInfo); for (lbIX=0 ; CPInfo.LeadByte[lbIX] != 0 ; lbIX+=2) { ped->DBCSVector[lbIX ] = CPInfo.LeadByte[lbIX]; ped->DBCSVector[lbIX+1] = CPInfo.LeadByte[lbIX+1]; } ped->DBCSVector[lbIX ] = 0x0; ped->DBCSVector[lbIX+1] = 0x0; } else { ped->DBCSVector[0] = 0x0; ped->DBCSVector[1] = 0x0; } // // Final check: if the font supports DBCS glyphs // // If we've got a font with DBCS glyphs, let's mark PED so. // But since the font's primary charset is the one other than FE, // we can only support UNICODE Edit control. // // a) GDI performs A/W conversion for ANSI apps based on the primary // character set in hDC, so it will break anyway. // b) ANSI applications are only supported on their native system locales: // GetACPCharSet() is expected to return a FE code page. // c) ANSI Edit control requires DBCSVector, which cannot be // initialized without a FE code page. // if (!ped->fAnsi) { FONTSIGNATURE fontSig; GetTextCharsetInfo(hdc, &fontSig, 0); if (fontSig.fsCsb[0] &FAREAST_CHARSET_BITS) { // // Since this is UNICODE, we're not // bDBCSCodePage = TRUE; } } return bDBCSCodePage; } //---------------------------------------------------------------------------// // // Edit_AnsiNext // // This function advances string pointer for Edit Control use only. // LPSTR Edit_AnsiNext(PED ped, LPSTR lpCurrent) { return lpCurrent+((Edit_IsDBCSLeadByte(ped,*lpCurrent)==TRUE) ? 2 : 1); } //---------------------------------------------------------------------------// // // Edit_AnsiPrev // // This function decrements string pointer for Edit Control use only. // LPSTR Edit_AnsiPrev(PED ped, LPSTR lpBase, LPSTR lpStr ) { LPSTR lpCurrent = lpStr -1; if (!ped->fDBCS) { // // just return ( lpStr - 1 ) // return lpCurrent; } if (lpBase >= lpCurrent) { return lpBase; } // // this check makes things faster // if (Edit_IsDBCSLeadByte(ped, *lpCurrent)) { return (lpCurrent - 1); } do { lpCurrent--; if (!Edit_IsDBCSLeadByte(ped, *lpCurrent)) { lpCurrent++; break; } } while(lpCurrent != lpBase); return lpStr - (((lpStr - lpCurrent) & 1) ? 1 : 2); } //---------------------------------------------------------------------------// // // Edit_NextIch // // This function advances string pointer for Edit Control use only. // ICH Edit_NextIch( PED ped, LPSTR pStart, ICH ichCurrent ) { if (!ped->fDBCS || !ped->fAnsi) { return (ichCurrent + 1); } else { ICH ichRet; LPSTR pText; if (pStart) { pText = pStart + ichCurrent; } else { pText = (LPSTR)Edit_Lock(ped) + ichCurrent; } ichRet = ichCurrent + ( Edit_IsDBCSLeadByte(ped, *pText) ? 2 : 1 ); if (!pStart) { Edit_Unlock(ped); } return ichRet; } } //---------------------------------------------------------------------------// // // Edit_PrevIch // // This function decrements string pointer for Edit Control use only. // ICH Edit_PrevIch(PED ped, LPSTR pStart, ICH ichCurrent) { LPSTR lpCurrent; LPSTR lpStr; LPSTR lpBase; if (!ped->fDBCS || !ped->fAnsi) { if (ichCurrent) { return (ichCurrent - 1); } else { return (ichCurrent); } } if (ichCurrent <= 1) { return 0; } if (pStart) { lpBase = pStart; } else { lpBase = Edit_Lock(ped); } lpStr = lpBase + ichCurrent; lpCurrent = lpStr - 1; if (Edit_IsDBCSLeadByte(ped,*lpCurrent)) { if (!pStart) { Edit_Unlock(ped); } return (ichCurrent - 2); } do { lpCurrent--; if (!Edit_IsDBCSLeadByte(ped, *lpCurrent)) { lpCurrent++; break; } } while(lpCurrent != lpBase); if (!pStart) { Edit_Unlock(ped); } return (ichCurrent - (((lpStr - lpCurrent) & 1) ? 1 : 2)); } //---------------------------------------------------------------------------// // // Edit_IsDBCSLeadByte // // IsDBCSLeadByte for Edit Control use only. // BOOL Edit_IsDBCSLeadByte(PED ped, BYTE cch) { INT i; if (!ped->fDBCS || !ped->fAnsi) { return (FALSE); } for (i = 0; ped->DBCSVector[i]; i += 2) { if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch)) { return (TRUE); } } return (FALSE); } //---------------------------------------------------------------------------// // // DbcsCombine // // Assemble two WM_CHAR messages to single DBCS character. // If program detects first byte of DBCS character in WM_CHAR message, // it calls this function to obtain second WM_CHAR message from queue. // finally this routine assembles first byte and second byte into single // DBCS character. // WORD DbcsCombine(HWND hwnd, WORD ch) { MSG msg; INT i = 10; // loop counter to avoid the infinite loop while (!PeekMessageA(&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) { if (--i == 0) return 0; Sleep(1); } return (WORD)ch | ((WORD)(msg.wParam) << 8); } //---------------------------------------------------------------------------// // // Edit_AdjustIch // // This function adjusts a current pointer correctly. If a current // pointer is lying between DBCS first byte and second byte, this // function adjusts a current pointer to a first byte of DBCS position // by decrement once. // ICH Edit_AdjustIch( PED ped, LPSTR lpstr, ICH ch ) { ICH newch = ch; if (!ped->fAnsi || !ped->fDBCS || newch == 0) { return ch; } if (!Edit_IsDBCSLeadByte(ped,lpstr[--newch])) { // // previous char is SBCS // return ch; } while (1) { if (!Edit_IsDBCSLeadByte(ped,lpstr[newch])) { newch++; break; } if (newch) { newch--; } else { break; } } return ((ch - newch) & 1) ? ch-1 : ch; } //---------------------------------------------------------------------------// // // Edit_AdjustIchNext // ICH Edit_AdjustIchNext(PED ped, LPSTR lpstr, ICH ch) { ICH ichNew = Edit_AdjustIch(ped,lpstr,ch); LPSTR lpnew = lpstr + ichNew; // // if ch > ichNew then Edit_AdjustIch adjusted ich. // if (ch > ichNew) { lpnew = Edit_AnsiNext(ped, lpnew); } return (ICH)(lpnew-lpstr); } //---------------------------------------------------------------------------// // // Edit_UpdateFormat // // Computes ped->format and ped->fRtoLReading from dwStyle and dwExStyle. // Refreshes the display if either are changed. // VOID Edit_UpdateFormat(PED ped, DWORD dwStyle, DWORD dwExStyle) { UINT fNewRtoLReading; UINT uiNewFormat; // // Extract new format and reading order from style // fNewRtoLReading = dwExStyle & WS_EX_RTLREADING ? 1 : 0; uiNewFormat = dwStyle & ES_FMTMASK; // // WS_EX_RIGHT is ignored unless dwStyle is ES_LEFT // if (uiNewFormat == ES_LEFT && dwExStyle & WS_EX_RIGHT) { uiNewFormat = ES_RIGHT; } // // Internally ES_LEFT and ES_RIGHT are swapped for RtoLReading order // (Think of them as ES_LEADING and ES_TRAILING) // if (fNewRtoLReading) { switch (uiNewFormat) { case ES_LEFT: uiNewFormat = ES_RIGHT; break; case ES_RIGHT: uiNewFormat = ES_LEFT; break; } } // // Format change does not cause redisplay by itself // ped->format = uiNewFormat; // // Refresh display on change of reading order // if (fNewRtoLReading != ped->fRtoLReading) { ped->fRtoLReading = fNewRtoLReading; if (ped->fWrap) { // // Redo wordwrap // EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL); EditML_UpdateiCaretLine(ped); } else { // // Refresh horizontal scrollbar display // EditML_Scroll(ped, FALSE, 0xffffffff, 0, TRUE); } Edit_InvalidateClient(ped, TRUE); } } //---------------------------------------------------------------------------// // // Edit_IsFullWidth // // Detects Far East FullWidth character. // BOOL Edit_IsFullWidth(DWORD dwCodePage,WCHAR wChar) { INT index; INT cChars; static struct _FULLWIDTH_UNICODE { WCHAR Start; WCHAR End; } FullWidthUnicodes[] = { { 0x4E00, 0x9FFF }, // CJK_UNIFIED_IDOGRAPHS { 0x3040, 0x309F }, // HIRAGANA { 0x30A0, 0x30FF }, // KATAKANA { 0xAC00, 0xD7A3 } // HANGUL }; // // Early out for ASCII. // if (wChar < 0x0080) { // // if the character < 0x0080, it should be a halfwidth character. // return FALSE; } // // Scan FullWdith definition table... most of FullWidth character is // defined here... this is more faster than call NLS API. // for (index = 0; index < ARRAYSIZE(FullWidthUnicodes); index++) { if ((wChar >= FullWidthUnicodes[index].Start) && (wChar <= FullWidthUnicodes[index].End)) { return TRUE; } } // // if this Unicode character is mapped to Double-Byte character, // this is also FullWidth character.. // cChars = WideCharToMultiByte((UINT)dwCodePage, 0, &wChar, 1, NULL, 0, NULL, NULL); return cChars > 1 ? TRUE : FALSE; }