Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

editec.c

Go to the documentation of this file.
00001 /****************************************************************************\ 00002 * editec.c - Edit controls rewrite. Version II of edit controls. 00003 * 00004 * Copyright (c) 1985 - 1999, Microsoft Corporation 00005 * 00006 * Created: 24-Jul-88 davidds 00007 \****************************************************************************/ 00008 00009 /* Warning: The single line editcontrols contain internal styles and API which 00010 * are need to support comboboxes. They are defined in combcom.h/combcom.inc 00011 * and may be redefined or renumbered as needed. 00012 */ 00013 00014 #include "precomp.h" 00015 #pragma hdrstop 00016 00017 LOOKASIDE EditLookaside; 00018 00019 ICH ECFindTabA(LPSTR lpstr, ICH cch); 00020 ICH ECFindTabW(LPWSTR lpstr, ICH cch); 00021 00022 #define umin(a, b) ((unsigned)(a) < (unsigned)(b) ? (unsigned)(a) : (unsigned)(b)) 00023 #define umax(a, b) ((unsigned)(a) > (unsigned)(b) ? (unsigned)(a) : (unsigned)(b)) 00024 00025 #define UNICODE_CARRIAGERETURN ((WCHAR)0x0d) 00026 #define UNICODE_LINEFEED ((WCHAR)0x0a) 00027 #define UNICODE_TAB ((WCHAR)0x09) 00028 00029 00030 // IME Menu IDs 00031 #define ID_IMEOPENCLOSE 10001 00032 #define ID_SOFTKBDOPENCLOSE 10002 00033 #define ID_RECONVERTSTRING 10003 00034 00035 typedef struct { 00036 DWORD fDisableCut : 1; 00037 DWORD fDisablePaste : 1; 00038 DWORD fNeedSeparatorBeforeImeMenu : 1; 00039 DWORD fIME : 1; 00040 } EditMenuItemState; 00041 00042 /***************************************************************************\ 00043 * Handlers common to both single and multi line edit controls. 00044 /***************************************************************************/ 00045 00046 /***************************************************************************\ 00047 * ECLock 00048 * 00049 * History: 00050 \***************************************************************************/ 00051 00052 PSTR ECLock( 00053 PED ped) 00054 { 00055 PSTR ptext = LOCALLOCK(ped->hText, ped->hInstance); 00056 ped->iLockLevel++; 00057 00058 /* 00059 * If this is the first lock of the text and the text is encoded 00060 * decode the text. 00061 */ 00062 //RIPMSG2(RIP_VERBOSE, "lock : %d '%10s'\n", ped->iLockLevel, ptext); 00063 if (ped->iLockLevel == 1 && ped->fEncoded) { 00064 /* 00065 * rtlrundecode can't handle zero length strings 00066 */ 00067 if (ped->cch != 0) { 00068 STRING string; 00069 string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar); 00070 string.Buffer = ptext; 00071 00072 RtlRunDecodeUnicodeString(ped->seed, (PUNICODE_STRING)&string); 00073 //RIPMSG1(RIP_VERBOSE, "Decoding: '%10s'\n", ptext); 00074 } 00075 ped->fEncoded = FALSE; 00076 } 00077 return ptext; 00078 } 00079 00080 /***************************************************************************\ 00081 * ECUnlock 00082 * 00083 * History: 00084 \***************************************************************************/ 00085 00086 void ECUnlock( 00087 PED ped) 00088 { 00089 /* 00090 * if we are removing the last lock on the text and the password 00091 * character is set then encode the text 00092 */ 00093 //RIPMSG1(RIP_VERBOSE, "unlock: %d '%10s'\n", ped->iLockLevel, ped->ptext); 00094 if (ped->charPasswordChar && ped->iLockLevel == 1 && ped->cch != 0) { 00095 UNICODE_STRING string; 00096 string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar); 00097 string.Buffer = LOCALLOCK(ped->hText, ped->hInstance); 00098 00099 RtlRunEncodeUnicodeString(&(ped->seed), &string); 00100 //RIPMSG1(RIP_VERBOSE, "Encoding: '%10s'\n", ped->ptext); 00101 ped->fEncoded = TRUE; 00102 LOCALUNLOCK(ped->hText, ped->hInstance); 00103 } 00104 LOCALUNLOCK(ped->hText, ped->hInstance); 00105 ped->iLockLevel--; 00106 } 00107 00108 /***************************************************************************\ 00109 * 00110 * GetActualNegA() 00111 * For a given strip of text, this function computes the negative A width 00112 * for the whole strip and returns the value as a postive number. 00113 * It also fills the NegAInfo structure with details about the postion 00114 * of this strip that results in this Negative A. 00115 * 00116 \***************************************************************************/ 00117 UINT GetActualNegA( 00118 HDC hdc, 00119 PED ped, 00120 int x, 00121 LPSTR lpstring, 00122 ICH ichString, 00123 int nCount, 00124 LPSTRIPINFO NegAInfo) 00125 { 00126 int iCharCount, i; 00127 int iLeftmostPoint = x; 00128 PABC pABCwidthBuff; 00129 UINT wCharIndex; 00130 int xStartPoint = x; 00131 ABC abc; 00132 00133 // To begin with, let us assume that there is no negative A width for 00134 // this strip and initialize accodingly. 00135 00136 NegAInfo->XStartPos = x; 00137 NegAInfo->lpString = lpstring; 00138 NegAInfo->nCount = 0; 00139 NegAInfo->ichString = ichString; 00140 00141 // If the current font is not a TrueType font, then there can not be any 00142 // negative A widths. 00143 if (!ped->fTrueType) { 00144 if(!ped->charOverhang) { 00145 return 0; 00146 } else { 00147 NegAInfo->nCount = min(nCount, (int)ped->wMaxNegAcharPos); 00148 return ped->charOverhang; 00149 } 00150 } 00151 00152 // How many characters are to be considered for computing Negative A ? 00153 iCharCount = min(nCount, (int)ped->wMaxNegAcharPos); 00154 00155 // Do we have the info on individual character's widths? 00156 if(!ped->charWidthBuffer) { 00157 // No! So, let us tell them to consider all the characters. 00158 NegAInfo->nCount = iCharCount; 00159 return(iCharCount * ped->aveCharWidth); 00160 } 00161 00162 pABCwidthBuff = (PABC) ped->charWidthBuffer; 00163 00164 if (ped->fAnsi) { 00165 for (i = 0; i < iCharCount; i++) { 00166 wCharIndex = (UINT)(*((unsigned char *)lpstring)); 00167 if (*lpstring == VK_TAB) { 00168 // To play it safe, we assume that this tab results in a tab length of 00169 // 1 pixel because this is the minimum possible tab length. 00170 x++; 00171 } else { 00172 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) 00173 x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width. 00174 else { 00175 GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc) ; 00176 x += abc.abcA; 00177 } 00178 00179 if (x < iLeftmostPoint) 00180 iLeftmostPoint = x; // Reset the leftmost point. 00181 if (x < xStartPoint) 00182 NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1. 00183 00184 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) { 00185 x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC; 00186 } else { 00187 x += abc.abcB + abc.abcC; 00188 } 00189 } 00190 00191 lpstring++; 00192 } 00193 } else { // Unicode 00194 LPWSTR lpwstring = (LPWSTR) lpstring ; 00195 00196 for (i = 0; i < iCharCount; i++) { 00197 wCharIndex = *lpwstring ; 00198 if (*lpwstring == VK_TAB) { 00199 // To play it safe, we assume that this tab results in a tab length of 00200 // 1 pixel because this is the minimum possible tab length. 00201 x++; 00202 } else { 00203 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) 00204 x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width. 00205 else { 00206 GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ; 00207 x += abc.abcA ; 00208 } 00209 00210 if (x < iLeftmostPoint) 00211 iLeftmostPoint = x; // Reset the leftmost point. 00212 if (x < xStartPoint) 00213 NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1. 00214 00215 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) 00216 x += pABCwidthBuff[wCharIndex].abcB + 00217 pABCwidthBuff[wCharIndex].abcC; 00218 else 00219 x += abc.abcB + abc.abcC ; 00220 } 00221 00222 lpwstring++; 00223 } 00224 } 00225 00226 // Let us return the negative A for the whole strip as a positive value. 00227 return((UINT)(xStartPoint - iLeftmostPoint)); 00228 } 00229 00230 00231 /***************************************************************************\ 00232 * 00233 * ECIsAncestorActive() 00234 * 00235 * Returns whether or not we're the child of an "active" window. Looks for 00236 * the first parent window that has a caption. 00237 * 00238 * This is a function because we might use it elsewhere when getting left 00239 * clicked on, etc. 00240 * 00241 \***************************************************************************/ 00242 BOOL ECIsAncestorActive(HWND hwnd) 00243 { 00244 // We want to return TRUE always for top level windows. That's because 00245 // of how WM_MOUSEACTIVATE works. If we see the click at all, the 00246 // window is active. However, if we reach a child ancestor that has 00247 // a caption, return the frame-on style bit. 00248 // 00249 // Note that calling FlashWindow() will have an effect. If the user 00250 // clicks on an edit field in a child window that is flashed off, nothing 00251 // will happen unless the window stops flashing and ncactivates first. 00252 00253 while (hwnd) { 00254 PWND pwnd = ValidateHwnd( hwnd ); 00255 // 00256 // Bail out if some parent window isn't 4.0 compatible or we've 00257 // reached the top. Fixes compatibility problems with 3.x apps, 00258 // especially MFC samples. 00259 // 00260 if (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WFCHILD)) 00261 hwnd = NULL; // to break us out of the loop 00262 else if (TestWF(pwnd, WFCPRESENT)) 00263 return(TestWF(pwnd, WFFRAMEON) != 0); 00264 else 00265 hwnd = GetParent(hwnd); 00266 } 00267 00268 return(TRUE); 00269 } 00270 00271 /***************************************************************************\ 00272 * ECSetIMEMenu() 00273 * 00274 * support IME specific context menu 00275 * 00276 * Create: 30-Apr-97 Hiroyama : Ported from Memphis 00277 \***************************************************************************/ 00278 BOOL ECSetIMEMenu( 00279 HMENU hMenu, 00280 HWND hwnd, 00281 EditMenuItemState state) 00282 { 00283 00284 MENUITEMINFO mii; 00285 HIMC hIMC; 00286 HKL hKL; 00287 HMENU hmenuSub; 00288 WCHAR szRes[32]; 00289 int nPrevLastItem; 00290 int nItemsAdded = 0; 00291 00292 UserAssert(IS_IME_ENABLED() && state.fIME); 00293 00294 hKL = THREAD_HKL(); 00295 if (!fpImmIsIME(hKL)) 00296 return TRUE; 00297 00298 hIMC = fpImmGetContext(hwnd); 00299 if (hIMC == NULL) { 00300 // early out 00301 return FALSE; 00302 } 00303 00304 hmenuSub = GetSubMenu(hMenu, 0); 00305 00306 if (hmenuSub == NULL) { 00307 return FALSE; 00308 } 00309 00310 nPrevLastItem = GetMenuItemCount(hmenuSub); 00311 00312 if (hIMC) { 00313 if (LOWORD(HandleToUlong(hKL)) != 0x412) { 00314 // 00315 // If Korean, do not show open/close menus 00316 // 00317 if (fpImmGetOpenStatus(hIMC)) 00318 LoadString(hmodUser, STR_IMECLOSE, szRes, sizeof(szRes)); 00319 else 00320 LoadString(hmodUser, STR_IMEOPEN, szRes, sizeof(szRes)); 00321 00322 mii.cbSize = sizeof(MENUITEMINFO); 00323 mii.fMask = MIIM_STRING | MIIM_ID; 00324 mii.dwTypeData = szRes; 00325 mii.cch = 0xffff; 00326 mii.wID = ID_IMEOPENCLOSE; 00327 InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); 00328 ++nItemsAdded; 00329 } 00330 00331 if (fpImmGetProperty(hKL, IGP_CONVERSION) & IME_CMODE_SOFTKBD) { 00332 DWORD fdwConversion; 00333 00334 fpImmGetConversionStatus(hIMC, &fdwConversion, NULL); 00335 00336 if (fdwConversion & IME_CMODE_SOFTKBD) 00337 LoadString(hmodUser, STR_SOFTKBDCLOSE, szRes, sizeof(szRes)); 00338 else 00339 LoadString(hmodUser, STR_SOFTKBDOPEN, szRes, sizeof(szRes)); 00340 00341 mii.cbSize = sizeof(MENUITEMINFO); 00342 mii.fMask = MIIM_STRING | MIIM_ID; 00343 mii.dwTypeData = szRes; 00344 mii.cch = 0xffff; 00345 mii.wID = ID_SOFTKBDOPENCLOSE; 00346 InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); 00347 ++nItemsAdded; 00348 } 00349 00350 if (LOWORD(HandleToUlong(hKL)) != 0x412) { 00351 // 00352 // If Korean, do not show reconversion menus 00353 // 00354 DWORD dwSCS = fpImmGetProperty(hKL, IGP_SETCOMPSTR); 00355 00356 LoadString(hmodUser, STR_RECONVERTSTRING, szRes, sizeof(szRes)); 00357 00358 mii.cbSize = sizeof(MENUITEMINFO); 00359 mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE; 00360 mii.dwTypeData = szRes; 00361 mii.fState = 0; 00362 mii.cch = 0xffff; 00363 mii.wID = ID_RECONVERTSTRING; 00364 00365 if (state.fDisableCut || 00366 !(dwSCS & SCS_CAP_SETRECONVERTSTRING) || 00367 !(dwSCS & SCS_CAP_MAKEREAD)) { 00368 mii.fState |= MFS_GRAYED; 00369 } 00370 00371 InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii); 00372 ++nItemsAdded; 00373 } 00374 } 00375 00376 // 00377 // Add or remove the menu separator 00378 // 00379 if (state.fNeedSeparatorBeforeImeMenu && nItemsAdded != 0) { 00380 // 00381 // If the menu for Middle East has left a separator, 00382 // fNeedSeparatorBeforeImeMenu is FALSE. 00383 // I.e. we don't need to add more. 00384 // 00385 mii.cbSize = sizeof(MENUITEMINFO); 00386 mii.fMask = MIIM_FTYPE; 00387 mii.fType = MFT_SEPARATOR; 00388 InsertMenuItem(hmenuSub, nPrevLastItem, TRUE, &mii); 00389 } 00390 else if (!state.fNeedSeparatorBeforeImeMenu && nItemsAdded == 0) { 00391 // 00392 // Extra separator is left by ME menus. Remove it. 00393 // 00394 UserVerify(NtUserDeleteMenu(hmenuSub, nPrevLastItem - 1, MF_BYPOSITION)); 00395 } 00396 00397 fpImmReleaseContext(hwnd, hIMC); 00398 00399 return TRUE; 00400 } 00401 00402 void ECInOutReconversionMode(PED ped, BOOL fIn) 00403 { 00404 UserAssert(fIn == TRUE || fIn == FALSE); 00405 if (fIn == ped->fInReconversion) { 00406 return; 00407 } 00408 ped->fInReconversion = fIn; 00409 if (ped->fFocus) { 00410 (fIn ? NtUserHideCaret: NtUserShowCaret)(ped->hwnd); 00411 } 00412 00413 return; 00414 } 00415 00416 /***************************************************************************\ 00417 * ECDoIMEMenuCommand() 00418 * 00419 * support IME specific context menu 00420 * 00421 * Create: 30-Apr-97 Hiroyama : Ported from Memphis 00422 \***************************************************************************/ 00423 BOOL NEAR ECDoIMEMenuCommand(PED ped, int cmd, HWND hwnd) 00424 { 00425 HIMC hIMC; 00426 00427 // early out 00428 switch (cmd) { 00429 case ID_IMEOPENCLOSE: 00430 case ID_SOFTKBDOPENCLOSE: 00431 case ID_RECONVERTSTRING: 00432 break; 00433 default: 00434 return FALSE; 00435 } 00436 00437 // everybody needs hIMC, so get it here 00438 hIMC = fpImmGetContext(hwnd); 00439 if (hIMC == NULL) { 00440 // indicate to caller, that no further command processing needed 00441 return TRUE; 00442 } 00443 00444 switch (cmd) { 00445 case ID_IMEOPENCLOSE: 00446 { 00447 // switch IME Open/Close status 00448 BOOL fOpen = fpImmGetOpenStatus(hIMC); 00449 00450 fpImmSetOpenStatus(hIMC, !fOpen); 00451 } 00452 break; 00453 00454 case ID_SOFTKBDOPENCLOSE: 00455 { 00456 DWORD fdwConversion; 00457 00458 if (fpImmGetConversionStatus(hIMC, &fdwConversion, NULL)) { 00459 // 00460 // Toggle soft keyboard Open/Close status 00461 // 00462 fpImmEnumInputContext(0, SyncSoftKbdState, 00463 (fdwConversion & IME_CMODE_SOFTKBD) != IME_CMODE_SOFTKBD); 00464 } 00465 } 00466 break; 00467 00468 case ID_RECONVERTSTRING: 00469 { 00470 DWORD dwStrLen; // holds TCHAR count of recionversion string 00471 DWORD cbLen; // holds BYTE SIZE of reconversion string 00472 DWORD dwSize; 00473 LPRECONVERTSTRING lpRCS; 00474 00475 // pass current selection to IME for reconversion 00476 dwStrLen = ped->ichMaxSel - ped->ichMinSel; 00477 cbLen = dwStrLen * ped->cbChar; 00478 dwSize = cbLen + sizeof(RECONVERTSTRING) + 8; 00479 00480 lpRCS = (LPRECONVERTSTRING)UserLocalAlloc(0, dwSize); 00481 00482 if (lpRCS) { 00483 LPBYTE pText; 00484 00485 pText = ECLock(ped); 00486 if (pText != NULL) { 00487 LPBYTE lpDest; 00488 BOOL (WINAPI* fpSetCompositionStringAW)(HIMC, DWORD, LPCVOID, DWORD, LPCVOID, DWORD); 00489 00490 lpRCS->dwSize = dwSize; 00491 lpRCS->dwVersion = 0; 00492 00493 lpRCS->dwStrLen = 00494 lpRCS->dwCompStrLen = 00495 lpRCS->dwTargetStrLen = dwStrLen; 00496 00497 lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); 00498 lpRCS->dwCompStrOffset = 00499 lpRCS->dwTargetStrOffset = 0; 00500 00501 lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING); 00502 00503 RtlCopyMemory(lpDest, pText + ped->ichMinSel * ped->cbChar, cbLen); 00504 if (ped->fAnsi) { 00505 LPBYTE psz = (LPBYTE)lpDest; 00506 psz[cbLen] = '\0'; 00507 fpSetCompositionStringAW = fpImmSetCompositionStringA; 00508 } else { 00509 LPWSTR pwsz = (LPWSTR)lpDest; 00510 pwsz[dwStrLen] = L'\0'; 00511 fpSetCompositionStringAW = fpImmSetCompositionStringW; 00512 } 00513 00514 ECUnlock(ped); 00515 00516 UserAssert(fpSetCompositionStringAW != NULL); 00517 00518 ECInOutReconversionMode(ped, TRUE); 00519 ECImmSetCompositionWindow(ped, 0, 0); // x and y will be overriden anyway 00520 fpSetCompositionStringAW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, dwSize, NULL, 0); 00521 } // pText 00522 UserLocalFree(lpRCS); 00523 } 00524 } 00525 break; 00526 00527 default: 00528 // should never reach here. 00529 RIPMSG1(RIP_ERROR, "ECDoIMEMenuCommand: unknown command id %d; should never reach here.", cmd); 00530 return FALSE; 00531 } 00532 00533 UserAssert(hIMC != NULL); 00534 fpImmReleaseContext(hwnd, hIMC); 00535 00536 return TRUE; 00537 } 00538 00539 /***************************************************************************\ 00540 * 00541 * ECMenu() 00542 * 00543 * Handles context menu for edit fields. Disables inappropriate commands. 00544 * Note that this is NOT subclassing friendly, like most of our functions, 00545 * for speed and convenience. 00546 * 00547 \***************************************************************************/ 00548 void ECMenu( 00549 HWND hwnd, 00550 PED ped, 00551 LPPOINT pt) 00552 { 00553 HMENU hMenu; 00554 int cmd = 0; 00555 int x; 00556 int y; 00557 EditMenuItemState state = { 00558 FALSE, // fDisableCut 00559 TRUE, // fDisablePaste 00560 TRUE, // fNeedSeparatorBeforeImeMenu 00561 IS_IME_ENABLED() && fpImmIsIME(THREAD_HKL()), // fIME 00562 }; 00563 00564 // Set focus if we don't have it. 00565 if (!ped->fFocus) 00566 NtUserSetFocus(hwnd); 00567 00568 // Grab the menu from USER's resources... 00569 if (!(hMenu = LoadMenu( hmodUser, MAKEINTRESOURCE( ID_EC_PROPERTY_MENU )))) 00570 return ; 00571 00572 00573 // Undo -- not allowed if we have no saved undo info 00574 if (ped->undoType == UNDO_NONE) 00575 EnableMenuItem(hMenu, WM_UNDO, MF_BYCOMMAND | MFS_GRAYED); 00576 00577 if (ped->fReadOnly || ped->charPasswordChar) { 00578 // Cut and Delete -- not allowed if read-only or password 00579 state.fDisableCut = TRUE; 00580 } else { 00581 // Cut, Delete -- not allowed if there's no selection 00582 if (ped->ichMinSel == ped->ichMaxSel) 00583 state.fDisableCut = TRUE; 00584 } 00585 // Paste -- not allowed if there's no text on the clipboard 00586 // (this works for both OEM and Unicode) 00587 // Used to be always disabled for password edits MCostea #221035 00588 00589 if (NtUserIsClipboardFormatAvailable(CF_TEXT)) 00590 state.fDisablePaste = FALSE; 00591 00592 if (state.fDisableCut) { 00593 EnableMenuItem(hMenu, WM_CUT, MF_BYCOMMAND | MFS_GRAYED); 00594 EnableMenuItem(hMenu, WM_CLEAR, MF_BYCOMMAND | MFS_GRAYED); 00595 } 00596 00597 if (state.fDisablePaste) 00598 EnableMenuItem(hMenu, WM_PASTE, MF_BYCOMMAND | MFS_GRAYED); 00599 00600 // Copy -- not allowed if there's no selection or password ec 00601 if ((ped->ichMinSel == ped->ichMaxSel) || (ped->charPasswordChar)) 00602 EnableMenuItem(hMenu, WM_COPY, MF_BYCOMMAND | MFS_GRAYED); 00603 00604 // Select All -- not allowed if there's no text or if everything is 00605 // selected. Latter case takes care of first one. 00606 if ((ped->ichMinSel == 0) && (ped->ichMaxSel == ped->cch)) 00607 EnableMenuItem(hMenu, EM_SETSEL, MF_BYCOMMAND | MFS_GRAYED); 00608 00609 if (ped->pLpkEditCallout) { 00610 ped->pLpkEditCallout->EditSetMenu(ped, hMenu); 00611 } else { 00612 NtUserDeleteMenu(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND); 00613 NtUserDeleteMenu(hMenu, ID_CNTX_RTL, MF_BYCOMMAND); 00614 NtUserDeleteMenu(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND); 00615 00616 if (state.fIME) { 00617 // One separator is left in the menu, 00618 // no need to add the one before IME menus 00619 state.fNeedSeparatorBeforeImeMenu = FALSE; 00620 } else { 00621 // Extra separator is left. Remove it. 00622 HMENU hmenuSub = GetSubMenu(hMenu, 0); 00623 int nItems = GetMenuItemCount(hmenuSub) - 1; 00624 00625 UserAssert(nItems >= 0); 00626 UserAssert(GetMenuState(hmenuSub, nItems, MF_BYPOSITION) & MF_SEPARATOR); 00627 // remove needless separator 00628 UserVerify(NtUserDeleteMenu(hmenuSub, nItems, MF_BYPOSITION)); 00629 } 00630 } 00631 00632 // IME specific menu 00633 if (state.fIME) { 00634 ECSetIMEMenu(hMenu, hwnd, state); 00635 } 00636 00637 // BOGUS 00638 // We position the menu below & to the right of the point clicked on. 00639 // Is this cool? I think so. Excel 4.0 does the same thing. It 00640 // seems like it would be neat if we could avoid obscuring the 00641 // selection. But in actuality, it seems even more awkward to move 00642 // the menu out of the way of the selection. The user can't click 00643 // and drag that way, and they have to move the mouse a ton. 00644 // 00645 // We need to use TPM_NONOTIFY because VBRUN100 and VBRUN200 GP-fault 00646 // on unexpected menu messages. 00647 // 00648 00649 /* 00650 * if message came via the keyboard then center on the control 00651 * We use -1 && -1 here not 0xFFFFFFFF like Win95 becuase we 00652 * previously converted the lParam to a point with sign extending. 00653 */ 00654 if (pt->x == -1 && pt->y == -1) { 00655 RECT rc; 00656 00657 GetWindowRect(hwnd, &rc); 00658 x = rc.left + (rc.right - rc.left) / 2; 00659 y = rc.top + (rc.bottom - rc.top) / 2; 00660 } else { 00661 x = pt->x; 00662 y = pt->y; 00663 } 00664 00665 cmd = NtUserTrackPopupMenuEx(GetSubMenu(hMenu, 0), TPM_NONOTIFY | 00666 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, 00667 x, y, hwnd, NULL); 00668 00669 // Free our menu 00670 NtUserDestroyMenu(hMenu); 00671 00672 if (cmd && (cmd != -1)) { 00673 if (ped->pLpkEditCallout && cmd) { 00674 ped->pLpkEditCallout->EditProcessMenu(ped, cmd); 00675 } 00676 if (!state.fIME || !ECDoIMEMenuCommand(ped, cmd, hwnd)) { 00677 // if cmd is not IME specific menu, send it. 00678 SendMessage(hwnd, cmd, 0, (cmd == EM_SETSEL) ? 0xFFFFFFFF : 0L ); 00679 } 00680 } 00681 } 00682 00683 00684 00685 /***************************************************************************\ 00686 * 00687 * ECClearText() 00688 * 00689 * Clears selected text. Does NOT _send_ a fake char backspace. 00690 * 00691 \***************************************************************************/ 00692 void ECClearText(PED ped) { 00693 if (!ped->fReadOnly && 00694 (ped->ichMinSel < ped->ichMaxSel)) { 00695 if (ped->fSingle) 00696 SLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L ); 00697 else 00698 MLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L ); 00699 } 00700 00701 } 00702 00703 00704 /***************************************************************************\ 00705 * 00706 * ECCutText() - 00707 * 00708 * Cuts selected text. This removes and copies the selection to the clip, 00709 * or if nothing is selected we delete (clear) the left character. 00710 * 00711 \***************************************************************************/ 00712 void ECCutText(PED ped) { 00713 // Cut selection--IE, remove and copy to clipboard, or if no selection, 00714 // delete (clear) character left. 00715 if (!ped->fReadOnly && 00716 (ped->ichMinSel < ped->ichMaxSel) && 00717 SendMessage(ped->hwnd, WM_COPY, 0, 0L)) { 00718 // If copy was successful, delete the copied text by sending a 00719 // backspace message which will redraw the text and take care of 00720 // notifying the parent of changes. 00721 ECClearText(ped); 00722 } 00723 } 00724 00725 /***************************************************************************\ 00726 * 00727 * ECGetModKeys() 00728 * 00729 * Gets modifier key states. Currently, we only check for VK_CONTROL and 00730 * VK_SHIFT. 00731 * 00732 \***************************************************************************/ 00733 int ECGetModKeys(int keyMods) { 00734 int scState; 00735 00736 scState = 0; 00737 00738 if (!keyMods) { 00739 if (GetKeyState(VK_CONTROL) < 0) 00740 scState |= CTRLDOWN; 00741 if (GetKeyState(VK_SHIFT) < 0) 00742 scState |= SHFTDOWN; 00743 } else if (keyMods != NOMODIFY) 00744 scState = keyMods; 00745 00746 return scState; 00747 } 00748 00749 /***************************************************************************\ 00750 * 00751 * ECTabTheTextOut() AorW 00752 * If fDrawText == FALSE, then this function returns the text extent of 00753 * of the given strip of text. It does not worry about the Negative widths. 00754 * 00755 * If fDrawText == TRUE, this draws the given strip of Text expanding the 00756 * tabs to proper lengths, calculates and fills up the NegCInfoForStrip with 00757 * details required to draw the portions of this strip that goes beyond the 00758 * xClipEndPos due to Negative C widths. 00759 * 00760 * Returns the max width AS A DWORD. We don't care about the height 00761 * at all. No one uses it. We keep a DWORD because that way we avoid 00762 * overflow. 00763 * 00764 * NOTE: If the language pack is loaded EcTabTheTextOut is not used - the 00765 * language pack must take care of all tab expansion and selection 00766 * highlighting with full support for bidi layout and complex script 00767 * glyph reordering. 00768 * 00769 \***************************************************************************/ 00770 UINT ECTabTheTextOut( 00771 HDC hdc, 00772 int xClipStPos, 00773 int xClipEndPos, 00774 int xStart, 00775 int y, 00776 LPSTR lpstring, 00777 int nCount, 00778 ICH ichString, 00779 PED ped, 00780 int iTabOrigin, 00781 BOOL fDraw, 00782 LPSTRIPINFO NegCInfoForStrip) 00783 { 00784 int nTabPositions; // Count of tabstops in tabstop array. 00785 LPINT lpintTabStopPositions; // Tab stop positions in pixels. 00786 00787 int cch; 00788 UINT textextent; 00789 int xEnd; 00790 int pixeltabstop = 0; 00791 int i; 00792 int cxCharWidth; 00793 RECT rc; 00794 BOOL fOpaque; 00795 BOOL fFirstPass = TRUE; 00796 PINT charWidthBuff; 00797 00798 int iTabLength; 00799 int nConsecutiveTabs; 00800 int xStripStPos; 00801 int xStripEndPos; 00802 int xEndOfStrip; 00803 STRIPINFO RedrawStripInfo; 00804 STRIPINFO NegAInfo; 00805 LPSTR lpTab; 00806 LPWSTR lpwTab; 00807 UINT wNegCwidth, wNegAwidth; 00808 int xRightmostPoint = xClipStPos; 00809 int xTabStartPos; 00810 int iSavedBkMode = 0; 00811 WCHAR wchar; 00812 SIZE size; 00813 ABC abc ; 00814 00815 // Algorithm: Draw the strip opaquely first. If a tab length is so 00816 // small that the portions of text on either side of a tab overlap with 00817 // the other, then this will result in some clipping. So, such portion 00818 // of the strip is remembered in "RedrawStripInfo" and redrawn 00819 // transparently later to compensate the clippings. 00820 // NOTE: "RedrawStripInfo" can hold info about just one portion. So, if 00821 // more than one portion of the strip needs to be redrawn transparently, 00822 // then we "merge" all such portions into a single strip and redraw that 00823 // strip at the end. 00824 00825 if (fDraw) { 00826 // To begin with, let us assume that there is no Negative C for this 00827 // strip and initialize the Negative Width Info structure. 00828 NegCInfoForStrip->nCount = 0; 00829 NegCInfoForStrip->XStartPos = xClipEndPos; 00830 00831 // We may not have to redraw any portion of this strip. 00832 RedrawStripInfo.nCount = 0; 00833 00834 fOpaque = (GetBkMode(hdc) == OPAQUE) || (fDraw == ECT_SELECTED); 00835 } 00836 #if DBG 00837 else { 00838 // 00839 // Both MLGetLineWidth() and ECCchInWidth() should be clipping 00840 // nCount to avoid overflow. 00841 // 00842 if (nCount > MAXLINELENGTH) 00843 RIPMSG0(RIP_WARNING, "ECTabTheTextOut: nCount > MAXLINELENGTH"); 00844 } 00845 #endif 00846 00847 // Let us define the Clip rectangle. 00848 rc.left = xClipStPos; 00849 rc.right = xClipEndPos; 00850 rc.top = y; 00851 rc.bottom = y + ped->lineHeight; 00852 00853 // Check if anything needs to be drawn. 00854 if (!lpstring || !nCount) { 00855 if (fDraw) 00856 ExtTextOutW(hdc, xClipStPos, y, 00857 (fOpaque ? ETO_OPAQUE | ETO_CLIPPED : ETO_CLIPPED), 00858 &rc, L"", 0, 0L); 00859 return(0L); 00860 } 00861 00862 // 00863 // Starting position 00864 // 00865 xEnd = xStart; 00866 00867 cxCharWidth = ped->aveCharWidth; 00868 00869 nTabPositions = (ped->pTabStops ? *(ped->pTabStops) : 0); 00870 if (ped->pTabStops) { 00871 lpintTabStopPositions = (LPINT)(ped->pTabStops+1); 00872 if (nTabPositions == 1) { 00873 pixeltabstop = lpintTabStopPositions[0]; 00874 if (!pixeltabstop) 00875 pixeltabstop = 1; 00876 } 00877 } else { 00878 lpintTabStopPositions = NULL; 00879 pixeltabstop = 8*cxCharWidth; 00880 } 00881 00882 // The first time we will draw the strip Opaquely. If some portions need 00883 // to be redrawn , then we will set the mode to TRANSPARENT and 00884 // jump to this location to redraw those portions. 00885 00886 RedrawStrip: 00887 while (nCount) { 00888 wNegCwidth = ped->wMaxNegC; 00889 00890 // Search for the first TAB in this strip; also compute the extent 00891 // of the the strip upto and not including the tab character. 00892 // 00893 // Note - If the langpack is loaded, there will be no charWidthBuffer. 00894 // 00895 if (ped->charWidthBuffer) { // Do we have a character width buffer? 00896 textextent = 0; 00897 cch = nCount; 00898 00899 if (ped->fTrueType) { // If so, does it have ABC widths? 00900 00901 UINT iRightmostPoint = 0; 00902 UINT wCharIndex; 00903 PABC pABCwidthBuff; 00904 00905 pABCwidthBuff = (PABC) ped->charWidthBuffer; 00906 00907 if ( ped->fAnsi ) { 00908 for (i = 0; i < nCount; i++) { 00909 00910 if (lpstring[i] == VK_TAB) { 00911 cch = i; 00912 break; 00913 } 00914 00915 wCharIndex = (UINT)(((unsigned char *)lpstring)[i]); 00916 if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { 00917 textextent += (UINT)(pABCwidthBuff[wCharIndex].abcA + 00918 pABCwidthBuff[wCharIndex].abcB); 00919 } else { // not in cache, will ask driver 00920 GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc); 00921 textextent += abc.abcA + abc.abcB ; 00922 } 00923 00924 if (textextent > iRightmostPoint) 00925 iRightmostPoint = textextent; 00926 00927 if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) { 00928 textextent += pABCwidthBuff[wCharIndex].abcC; 00929 } else { // not in cache 00930 textextent += abc.abcC; 00931 } 00932 00933 if (textextent > iRightmostPoint) 00934 iRightmostPoint = textextent; 00935 } 00936 00937 } else { // Unicode 00938 for (i = 0; i < nCount; i++) { 00939 WCHAR UNALIGNED * lpwstring = (WCHAR UNALIGNED *)lpstring; 00940 00941 if (lpwstring[i] == VK_TAB) { 00942 cch = i; 00943 break; 00944 } 00945 00946 wCharIndex = lpwstring[i] ; 00947 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) 00948 textextent += pABCwidthBuff[wCharIndex].abcA + 00949 pABCwidthBuff[wCharIndex].abcB; 00950 else { 00951 GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ; 00952 textextent += abc.abcA + abc.abcB ; 00953 } 00954 00955 /* 00956 * Note that abcC could be negative so we need this 00957 * statement here *and* below 00958 */ 00959 if (textextent > iRightmostPoint) 00960 iRightmostPoint = textextent; 00961 00962 if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) 00963 textextent += pABCwidthBuff[wCharIndex].abcC; 00964 else 00965 textextent += abc.abcC ; 00966 00967 if (textextent > iRightmostPoint) 00968 iRightmostPoint = textextent; 00969 } 00970 } 00971 00972 wNegCwidth = (int)(iRightmostPoint - textextent); 00973 } else { // !ped->fTrueType 00974 // No! This is not a TrueType font; So, we have only character 00975 // width info in this buffer. 00976 00977 charWidthBuff = ped->charWidthBuffer; 00978 00979 if ( ped->fAnsi ) { 00980 // Initially assume no tabs exist in the text so cch=nCount. 00981 for (i = 0; i < nCount; i++) { 00982 if (lpstring[i] == VK_TAB) { 00983 cch = i; 00984 break; 00985 } 00986 00987 // 00988 // Call GetTextExtentPoint for dbcs/hankaku characters 00989 // 00990 if (ped->fDBCS && (i+1 < nCount) 00991 && ECIsDBCSLeadByte(ped,lpstring[i])) { 00992 GetTextExtentPointA(hdc, &lpstring[i], 2, &size); 00993 textextent += size.cx; 00994 i++; 00995 } else if ((UCHAR)lpstring[i] >= CHAR_WIDTH_BUFFER_LENGTH) { 00996 // Skip this GetExtentPoint call for non hankaku code points 00997 // Or if the character is in the width cache. 00998 GetTextExtentPointA(hdc, &lpstring[i], 1, &size); 00999 textextent += size.cx; 01000 } else { 01001 textextent += (UINT)(charWidthBuff[(UINT)(((unsigned char *)lpstring)[i])]); 01002 } 01003 } 01004 } else { 01005 LPWSTR lpwstring = (LPWSTR) lpstring ; 01006 INT cchUStart; // start of unicode character count 01007 01008 for (i = 0; i < nCount; i++) { 01009 if (lpwstring[i] == VK_TAB) { 01010 cch = i; 01011 break; 01012 } 01013 01014 wchar = lpwstring[i]; 01015 if (wchar >= CHAR_WIDTH_BUFFER_LENGTH) { 01016 01017 /* 01018 * We have a Unicode character that is not in our 01019 * cache, get all the characters outside the cache 01020 * before getting the text extent on this part of the 01021 * string. 01022 */ 01023 cchUStart = i; 01024 while (wchar >= CHAR_WIDTH_BUFFER_LENGTH && 01025 wchar != VK_TAB && i < nCount) { 01026 wchar = lpwstring[++i]; 01027 } 01028 01029 GetTextExtentPointW(hdc, (LPWSTR)lpwstring + cchUStart, 01030 i-cchUStart, &size); 01031 textextent += size.cx; 01032 01033 01034 if (wchar == VK_TAB || i >= nCount) { 01035 cch = i; 01036 break; 01037 } 01038 /* 01039 * We have a char that is in the cache, fall through. 01040 */ 01041 } 01042 /* 01043 * The width of this character is in the cache buffer. 01044 */ 01045 textextent += ped->charWidthBuffer[wchar]; 01046 } 01047 } 01048 } // fTrueType else. 01049 01050 nCount -= cch; 01051 } else { // If we don't have a buffer that contains the width info. 01052 /* 01053 * Gotta call the driver to do our text extent. 01054 */ 01055 01056 if ( ped->fAnsi ) { 01057 cch = (int)ECFindTabA(lpstring, nCount); 01058 GetTextExtentPointA(hdc, lpstring, cch, &size) ; 01059 } else { 01060 cch = (int)ECFindTabW((LPWSTR) lpstring, nCount); 01061 GetTextExtentPointW(hdc, (LPWSTR)lpstring, cch, &size); 01062 } 01063 nCount -= cch; 01064 // 01065 // Subtruct Overhang for Italic fonts. 01066 // 01067 textextent = (size.cx - ped->charOverhang); 01068 } 01069 01070 // 01071 // textextent is computed. 01072 // 01073 01074 xStripStPos = xEnd; 01075 xEnd += (int)textextent; 01076 xStripEndPos = xEnd; 01077 01078 // We will consider the negative widths only if when we draw opaquely. 01079 if (fFirstPass && fDraw) { 01080 xRightmostPoint = max(xStripEndPos + (int)wNegCwidth, xRightmostPoint); 01081 01082 // Check if this strip peeps beyond the clip region. 01083 if (xRightmostPoint > xClipEndPos) { 01084 if (!NegCInfoForStrip->nCount) { 01085 NegCInfoForStrip->lpString = lpstring; 01086 NegCInfoForStrip->ichString = ichString; 01087 NegCInfoForStrip->nCount = nCount+cch; 01088 NegCInfoForStrip->XStartPos = xStripStPos; 01089 } 01090 } 01091 } /* if (fFirstPass && fDraw) */ 01092 01093 if ( ped->fAnsi ) 01094 lpTab = lpstring + cch; // Possibly Points to a tab character. 01095 else 01096 lpwTab = ((LPWSTR)lpstring) + cch ; 01097 01098 // we must consider all the consecutive tabs and calculate the 01099 // the begining of next strip. 01100 nConsecutiveTabs = 0; 01101 while (nCount && 01102 (ped->fAnsi ? (*lpTab == VK_TAB) : (*lpwTab == VK_TAB))) { 01103 // Find the next tab position and update the x value. 01104 xTabStartPos = xEnd; 01105 if (pixeltabstop) 01106 xEnd = (((xEnd-iTabOrigin)/pixeltabstop)*pixeltabstop) + 01107 pixeltabstop + iTabOrigin; 01108 else { 01109 for (i = 0; i < nTabPositions; i++) { 01110 if (xEnd < (lpintTabStopPositions[i] + iTabOrigin)) { 01111 xEnd = (lpintTabStopPositions[i] + iTabOrigin); 01112 break; 01113 } 01114 } 01115 01116 // Check if all the tabstops set are exhausted; Then start using 01117 // default tab stop positions. 01118 if (i == nTabPositions) { 01119 pixeltabstop = 8*cxCharWidth; 01120 xEnd = ((xEnd - iTabOrigin)/pixeltabstop)*pixeltabstop + 01121 pixeltabstop + iTabOrigin; 01122 } 01123 } 01124 01125 if (fFirstPass && fDraw) { 01126 xRightmostPoint = max(xEnd, xRightmostPoint); 01127 01128 /* Check if this strip peeps beyond the clip region */ 01129 if (xRightmostPoint > xClipEndPos) { 01130 if (!NegCInfoForStrip->nCount) { 01131 NegCInfoForStrip->ichString = ichString + cch + nConsecutiveTabs; 01132 NegCInfoForStrip->nCount = nCount; 01133 NegCInfoForStrip->lpString = (ped->fAnsi ? 01134 lpTab : (LPSTR) lpwTab); 01135 NegCInfoForStrip->XStartPos = xTabStartPos; 01136 } 01137 } 01138 } /* if(fFirstPass) */ 01139 01140 nConsecutiveTabs++; 01141 nCount--; 01142 ped->fAnsi ? lpTab++ : (LPSTR) (lpwTab++) ; // Move to the next character. 01143 } // while(*lpTab == TAB) // 01144 01145 if (fDraw) { 01146 if (fFirstPass) { 01147 // Is anything remaining to be drawn in this strip? 01148 if (!nCount) 01149 rc.right = xEnd; // No! We are done. 01150 else { 01151 // "x" is the effective starting position of next strip. 01152 iTabLength = xEnd - xStripEndPos; 01153 01154 // Check if there is a possibility of this tab length being too small 01155 // compared to the negative A and C widths if any. 01156 if ((wNegCwidth + (wNegAwidth = ped->wMaxNegA)) > (UINT)iTabLength) { 01157 // Unfortunately, there is a possiblity of an overlap. 01158 // Let us find out the actual NegA for the next strip. 01159 wNegAwidth = GetActualNegA( 01160 hdc, 01161 ped, 01162 xEnd, 01163 lpstring + (cch + nConsecutiveTabs)*ped->cbChar, 01164 ichString + cch + nConsecutiveTabs, 01165 nCount, 01166 &NegAInfo); 01167 } 01168 01169 // Check if they actually overlap // 01170 if ((wNegCwidth + wNegAwidth) <= (UINT)iTabLength) { 01171 // No overlap between the strips. This is the ideal situation. 01172 rc.right = xEnd - wNegAwidth; 01173 } else { 01174 // Yes! They overlap. 01175 rc.right = xEnd; 01176 01177 // See if negative C width is too large compared to tab length. 01178 if (wNegCwidth > (UINT)iTabLength) { 01179 // Must redraw transparently a part of the current strip later. 01180 if (RedrawStripInfo.nCount) { 01181 // A previous strip also needs to be redrawn; So, merge this 01182 // strip to that strip. 01183 RedrawStripInfo.nCount = (ichString - 01184 RedrawStripInfo.ichString) + cch; 01185 } else { 01186 RedrawStripInfo.nCount = cch; 01187 RedrawStripInfo.lpString = lpstring; 01188 RedrawStripInfo.ichString = ichString; 01189 RedrawStripInfo.XStartPos = xStripStPos; 01190 } 01191 } 01192 01193 if (wNegAwidth) { 01194 // Must redraw transparently the first part of the next strip later. 01195 if (RedrawStripInfo.nCount) { 01196 // A previous strip also needs to be redrawn; So, merge this 01197 // strip to that strip. 01198 RedrawStripInfo.nCount = (NegAInfo.ichString - RedrawStripInfo.ichString) + 01199 NegAInfo.nCount; 01200 } else 01201 RedrawStripInfo = NegAInfo; 01202 } 01203 } 01204 } // else (!nCount) // 01205 } // if (fFirstPass) // 01206 01207 if (rc.left < xClipEndPos) { 01208 if (fFirstPass) { 01209 // If this is the end of the strip, then complete the rectangle. 01210 if ((!nCount) && (xClipEndPos == MAXCLIPENDPOS)) 01211 rc.right = max(rc.right, xClipEndPos); 01212 else 01213 rc.right = min(rc.right, xClipEndPos); 01214 } 01215 01216 // Draw the current strip. 01217 if (rc.left < rc.right) 01218 if ( ped->fAnsi ) 01219 ExtTextOutA(hdc, 01220 xStripStPos, 01221 y, 01222 (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED), 01223 (LPRECT)&rc, lpstring, cch, 0L); 01224 else 01225 ExtTextOutW(hdc, 01226 xStripStPos, 01227 y, 01228 (fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED), 01229 (LPRECT)&rc, (LPWSTR)lpstring, cch, 0L); 01230 01231 } 01232 01233 if (fFirstPass) 01234 rc.left = max(rc.right, xClipStPos); 01235 ichString += (cch+nConsecutiveTabs); 01236 } // if (fDraw) // 01237 01238 // Skip over the tab and the characters we just drew. 01239 lpstring += (cch + nConsecutiveTabs) * ped->cbChar; 01240 } // while (nCount) // 01241 01242 xEndOfStrip = xEnd; 01243 01244 // check if we need to draw some portions transparently. 01245 if (fFirstPass && fDraw && RedrawStripInfo.nCount) { 01246 iSavedBkMode = SetBkMode(hdc, TRANSPARENT); 01247 fFirstPass = FALSE; 01248 01249 nCount = RedrawStripInfo.nCount; 01250 rc.left = xClipStPos; 01251 rc.right = xClipEndPos; 01252 lpstring = RedrawStripInfo.lpString; 01253 ichString = RedrawStripInfo.ichString; 01254 xEnd = RedrawStripInfo.XStartPos; 01255 goto RedrawStrip; // Redraw Transparently. 01256 } 01257 01258 if (iSavedBkMode) // Did we change the Bk mode? 01259 SetBkMode(hdc, iSavedBkMode); // Then, let us set it back! 01260 01261 return((UINT)(xEndOfStrip - xStart)); 01262 } 01263 01264 01265 01266 /***************************************************************************\ 01267 * ECCchInWidth AorW 01268 * 01269 * Returns maximum count of characters (up to cch) from the given 01270 * string (starting either at the beginning and moving forward or at the 01271 * end and moving backwards based on the setting of the fForward flag) 01272 * which will fit in the given width. ie. Will tell you how much of 01273 * lpstring will fit in the given width even when using proportional 01274 * characters. WARNING: If we use kerning, then this loses... 01275 * 01276 * History: 01277 * 01278 * NOTE: ECCchInWidth is not called if the language pack is loaded. 01279 \***************************************************************************/ 01280 01281 ICH ECCchInWidth( 01282 PED ped, 01283 HDC hdc, 01284 LPSTR lpText, 01285 ICH cch, 01286 int width, 01287 BOOL fForward) 01288 { 01289 int stringExtent; 01290 int cchhigh; 01291 int cchnew = 0; 01292 int cchlow = 0; 01293 SIZE size; 01294 LPSTR lpStart; 01295 01296 if ((width <= 0) || !cch) 01297 return (0); 01298 01299 /* 01300 * Optimize nonproportional fonts for single line ec since they don't have 01301 * tabs. 01302 */ 01303 // 01304 // Change optimize condition for fixed pitch font 01305 // 01306 if (ped->fNonPropFont && ped->fSingle && !ped->fDBCS) { 01307 return (ECAdjustIch( ped, lpText, umin(width/ped->aveCharWidth,(int)cch))); 01308 } 01309 01310 /* 01311 * Check if password hidden chars are being used. 01312 */ 01313 if (ped->charPasswordChar) { 01314 return (umin(width / ped->cPasswordCharWidth, (int)cch)); 01315 } 01316 01317 /* 01318 * ALWAYS RESTRICT TO AT MOST MAXLINELENGTH to avoid overflow... 01319 */ 01320 cch = umin(MAXLINELENGTH, cch); 01321 01322 cchhigh = cch + 1; 01323 while (cchlow < cchhigh - 1) { 01324 cchnew = umax((cchhigh - cchlow) / 2, 1) + cchlow; 01325 01326 lpStart = lpText; 01327 01328 /* 01329 * If we want to figure out how many fit starting at the end and moving 01330 * backwards, make sure we move to the appropriate position in the 01331 * string before calculating the text extent. 01332 */ 01333 if (!fForward) 01334 lpStart += (cch - cchnew)*ped->cbChar; 01335 01336 if (ped->fSingle) { 01337 if (ped->fAnsi) 01338 GetTextExtentPointA(hdc, (LPSTR)lpStart, cchnew, &size); 01339 else 01340 GetTextExtentPointW(hdc, (LPWSTR)lpStart, cchnew, &size); 01341 stringExtent = size.cx; 01342 } else { 01343 stringExtent = ECTabTheTextOut(hdc, 0, 0, 0, 0, 01344 lpStart, 01345 cchnew, 0, 01346 ped, 0, ECT_CALC, NULL ); 01347 } 01348 01349 if (stringExtent > width) { 01350 cchhigh = cchnew; 01351 } else { 01352 cchlow = cchnew; 01353 } 01354 } 01355 // 01356 // Call ECAdjustIch ( generic case ) 01357 // 01358 cchlow = ECAdjustIch( ped, lpText, cchlow ); 01359 return (cchlow); 01360 } 01361 01362 /***************************************************************************\ 01363 * ECFindTab 01364 * 01365 * Scans lpstr and return s the number of CHARs till the first TAB. 01366 * Scans at most cch chars of lpstr. 01367 * 01368 * History: 01369 \***************************************************************************/ 01370 01371 ICH ECFindTabA( 01372 LPSTR lpstr, 01373 ICH cch) 01374 { 01375 LPSTR copylpstr = lpstr; 01376 01377 if (!cch) 01378 return 0; 01379 01380 while (*lpstr != VK_TAB) { 01381 lpstr++; 01382 if (--cch == 0) 01383 break; 01384 } 01385 return ((ICH)(lpstr - copylpstr)); 01386 } 01387 01388 ICH ECFindTabW( 01389 LPWSTR lpstr, 01390 ICH cch) 01391 { 01392 LPWSTR copylpstr = lpstr; 01393 01394 if (!cch) 01395 return 0; 01396 01397 while (*lpstr != VK_TAB) { 01398 lpstr++; 01399 if (--cch == 0) 01400 break; 01401 } 01402 return ((ICH)(lpstr - copylpstr)); 01403 } 01404 01405 /***************************************************************************\ 01406 * 01407 * ECGetBrush() 01408 * 01409 * Gets appropriate background brush to erase with. 01410 * 01411 \***************************************************************************/ 01412 HBRUSH ECGetBrush(PED ped, HDC hdc) 01413 { 01414 HBRUSH hbr; 01415 BOOL f40Compat; 01416 01417 f40Compat = (GETAPPVER() >= VER40); 01418 01419 // Get background brush 01420 if ((ped->fReadOnly || ped->fDisabled) && f40Compat) { 01421 hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLORSTATIC); 01422 } else 01423 hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLOREDIT); 01424 01425 if (ped->fDisabled && (ped->fSingle || f40Compat)) { 01426 DWORD rgb; 01427 01428 // Change text color 01429 rgb = GetSysColor(COLOR_GRAYTEXT); 01430 if (rgb != GetBkColor(hdc)) 01431 SetTextColor(hdc, rgb); 01432 } 01433 return(hbr); 01434 } 01435 01436 01437 /***************************************************************************\ 01438 * NextWordCallBack 01439 * 01440 * 01441 * 01442 * History: 01443 * 02-19-92 JimA Ported from Win31 sources. 01444 \***************************************************************************/ 01445 01446 void NextWordCallBack( 01447 PED ped, 01448 ICH ichStart, 01449 BOOL fLeft, 01450 ICH *pichMin, 01451 ICH *pichMax ) 01452 { 01453 ICH ichMinSel; 01454 ICH ichMaxSel; 01455 LPSTR pText; 01456 01457 pText = ECLock(ped); 01458 01459 if (fLeft || (!(BOOL)CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, 01460 ichStart, ped->cch, WB_ISDELIMITER) && 01461 (ped->fAnsi ? (*(pText + ichStart) != VK_RETURN) : (*((LPWSTR)pText + ichStart) != VK_RETURN)) 01462 )) 01463 ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_LEFT); 01464 else 01465 ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_RIGHT); 01466 01467 ichMaxSel = min(ichMinSel + 1, ped->cch); 01468 01469 if (ped->fAnsi) { 01470 if (*(pText + ichMinSel) == VK_RETURN) { 01471 if (ichMinSel > 0 && *(pText + ichMinSel - 1) == VK_RETURN) { 01472 01473 /* 01474 * So that we can treat CRCRLF as one word also. 01475 */ 01476 ichMinSel--; 01477 } else if (*(pText+ichMinSel + 1) == VK_RETURN) { 01478 01479 /* 01480 * Move MaxSel on to the LF 01481 */ 01482 ichMaxSel++; 01483 } 01484 } 01485 } else { 01486 if (*((LPWSTR)pText + ichMinSel) == VK_RETURN) { 01487 if (ichMinSel > 0 && *((LPWSTR)pText + ichMinSel - 1) == VK_RETURN) { 01488 01489 /* 01490 * So that we can treat CRCRLF as one word also. 01491 */ 01492 ichMinSel--; 01493 } else if (*((LPWSTR)pText+ichMinSel + 1) == VK_RETURN) { 01494 01495 /* 01496 * Move MaxSel on to the LF 01497 */ 01498 ichMaxSel++; 01499 } 01500 } 01501 } 01502 ichMaxSel = CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichMaxSel, ped->cch, WB_RIGHT); 01503 ECUnlock(ped); 01504 01505 if (pichMin) *pichMin = ichMinSel; 01506 if (pichMax) *pichMax = ichMaxSel; 01507 } 01508 01509 /***************************************************************************\ 01510 * NextWordLpkCallback 01511 * 01512 * Identifies next/prev word position for complex scripts 01513 * 01514 * History: 01515 * 04-22-97 DBrown 01516 \***************************************************************************/ 01517 01518 void NextWordLpkCallBack( 01519 PED ped, 01520 ICH ichStart, 01521 BOOL fLeft, 01522 ICH *pichMin, 01523 ICH *pichMax) 01524 { 01525 PSTR pText = ECLock(ped); 01526 HDC hdc = ECGetEditDC(ped, TRUE); 01527 01528 ped->pLpkEditCallout->EditNextWord(ped, hdc, pText, ichStart, fLeft, pichMin, pichMax); 01529 01530 ECReleaseEditDC(ped, hdc, TRUE); 01531 ECUnlock(ped); 01532 } 01533 01534 /***************************************************************************\ 01535 * ECWordAorW 01536 * 01537 * if fLeft, Returns the ichMinSel and ichMaxSel of the word to the 01538 * left of ichStart. ichMinSel contains the starting letter of the word, 01539 * ichmaxsel contains all spaces up to the first character of the next word. 01540 * 01541 * if !fLeft, Returns the ichMinSel and ichMaxSel of the word to the right of 01542 * ichStart. ichMinSel contains the starting letter of the word, ichmaxsel 01543 * contains the first letter of the next word. If ichStart is in the middle 01544 * of a word, that word is considered the left or right word. 01545 * 01546 * A CR LF pair or CRCRLF triple is considered a single word in 01547 * multiline edit controls. 01548 * 01549 * History: 01550 \***************************************************************************/ 01551 01552 void ECWord( 01553 PED ped, 01554 ICH ichStart, 01555 BOOL fLeft, 01556 ICH *pichMin, 01557 ICH *pichMax ) 01558 { 01559 BOOL charLocated = FALSE; 01560 BOOL spaceLocated = FALSE; 01561 01562 if ((!ichStart && fLeft) || (ichStart == ped->cch && !fLeft)) { 01563 01564 /* 01565 * We are at the beginning of the text (looking left) or we are at end 01566 * of text (looking right), no word here 01567 */ 01568 if (pichMin) *pichMin=0; 01569 if (pichMax) *pichMax=0; 01570 return; 01571 } 01572 01573 /* 01574 * Don't give out hints about word breaks if password chars are being used, 01575 */ 01576 if (ped->charPasswordChar) { 01577 if (pichMin) *pichMin=0; 01578 if (pichMax) *pichMax=ped->cch; 01579 return; 01580 } 01581 01582 if (ped->fAnsi) { 01583 PSTR pText; 01584 PSTR pWordMinSel; 01585 PSTR pWordMaxSel; 01586 PSTR pPrevChar; 01587 01588 UserAssert(ped->cbChar == sizeof(CHAR)); 01589 01590 if (ped->lpfnNextWord) { 01591 NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax); 01592 return; 01593 } 01594 01595 if (ped->pLpkEditCallout) { 01596 NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax); 01597 return; 01598 } 01599 01600 pText = ECLock(ped); 01601 pWordMinSel = pWordMaxSel = pText + ichStart; 01602 01603 /* 01604 * if fLeft: Move pWordMinSel to the left looking for the start of a word. 01605 * If we start at a space, we will include spaces in the selection as we 01606 * move left untill we find a nonspace character. At that point, we continue 01607 * looking left until we find a space. Thus, the selection will consist of 01608 * a word with its trailing spaces or, it will consist of any leading at the 01609 * beginning of a line of text. 01610 */ 01611 01612 /* 01613 * if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a 01614 * word. If the pWordMinSel points to a character, then we move left 01615 * looking for a space which will signify the start of the word. If 01616 * pWordMinSel points to a space, we look right till we come upon a 01617 * character. pMaxWord will look right starting at pMinWord looking for the 01618 * end of the word and its trailing spaces. 01619 */ 01620 01621 if (fLeft || !ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0D) { 01622 01623 /* 01624 * If we are moving left or if we are moving right and we are not on a 01625 * space or a CR (the start of a word), then we was look left for the 01626 * start of a word which is either a CR or a character. We do this by 01627 * looking left till we find a character (or if CR we stop), then we 01628 * continue looking left till we find a space or LF. 01629 */ 01630 while (pWordMinSel > pText && ((!ISDELIMETERA(*(pWordMinSel - 1)) && 01631 *(pWordMinSel - 1) != 0x0A) || !charLocated)) { 01632 01633 /* 01634 * Treat double byte character as a word ( in ansi pWordMinSel loop ) 01635 */ 01636 pPrevChar = ECAnsiPrev( ped, pText, pWordMinSel ); 01637 01638 /* 01639 ** we are looking right ( !fLeft ). 01640 ** if current character is a double byte chararacter or 01641 ** previous character is a double byte character, we 01642 ** are on the beggining of a word. 01643 */ 01644 if ( !fLeft && ( ISDELIMETERA( *pPrevChar ) || 01645 *pPrevChar == 0x0A || 01646 ECIsDBCSLeadByte(ped, *pWordMinSel) || 01647 pWordMinSel - pPrevChar == 2 ) ) { 01648 /* 01649 * If we are looking for the start of the word right, then we 01650 * stop when we have found it. (needed in case charLocated is 01651 * still FALSE) 01652 */ 01653 break; 01654 } 01655 01656 if ( pWordMinSel - pPrevChar == 2 ) { 01657 /* 01658 ** previous character is a double byte character. 01659 ** if we are in a word ( charLocated == TRUE ) 01660 ** current position is the beginning of the word 01661 ** if we are not in a word ( charLocated == FALSE ) 01662 ** the previous character is what we looking for. 01663 */ 01664 if ( ! charLocated ) { 01665 pWordMinSel = pPrevChar; 01666 } 01667 break; 01668 } 01669 pWordMinSel = pPrevChar; 01670 01671 if (!ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0A) { 01672 01673 /* 01674 * We have found the last char in the word. Continue looking 01675 * backwards till we find the first char of the word 01676 */ 01677 charLocated = TRUE; 01678 01679 /* 01680 * We will consider a CR the start of a word 01681 */ 01682 if (*pWordMinSel == 0x0D) 01683 break; 01684 } 01685 } 01686 } else { 01687 while ((ISDELIMETERA(*pWordMinSel) || *pWordMinSel == 0x0A) && pWordMinSel < pText + ped->cch) 01688 pWordMinSel++; 01689 } 01690 01691 /* 01692 * Adjust the initial position of pWordMaxSel ( in ansi ) 01693 */ 01694 pWordMaxSel = ECAnsiNext(ped, pWordMinSel); 01695 pWordMaxSel = min(pWordMaxSel, pText + ped->cch); 01696 01697 /* 01698 ** If pWordMinSel points a double byte character AND 01699 ** pWordMaxSel points non space 01700 ** then 01701 ** pWordMaxSel points the beggining of next word. 01702 */ 01703 if ( ( pWordMaxSel - pWordMinSel == 2 ) && ! ISDELIMETERA(*pWordMaxSel) ) 01704 goto FastReturnA; 01705 if (*pWordMinSel == 0x0D) { 01706 if (pWordMinSel > pText && *(pWordMinSel - 1) == 0x0D) 01707 /* So that we can treat CRCRLF as one word also. */ 01708 pWordMinSel--; 01709 else if (*(pWordMinSel + 1) == 0x0D) 01710 /* Move MaxSel on to the LF */ 01711 pWordMaxSel++; 01712 } 01713 01714 01715 01716 /* 01717 * Check if we have a one character word 01718 */ 01719 if (ISDELIMETERA(*pWordMaxSel)) 01720 spaceLocated = TRUE; 01721 01722 /* 01723 * Move pWordMaxSel to the right looking for the end of a word and its 01724 * trailing spaces. WordMaxSel stops on the first character of the next 01725 * word. Thus, we break either at a CR or at the first nonspace char after 01726 * a run of spaces or LFs. 01727 */ 01728 while ((pWordMaxSel < pText + ped->cch) && (!spaceLocated || (ISDELIMETERA(*pWordMaxSel)))) { 01729 if (*pWordMaxSel == 0x0D) 01730 break; 01731 01732 /* 01733 * Treat double byte character as a word ( in ansi pWordMaxSel loop ) 01734 */ 01735 /* 01736 ** if it's a double byte character then 01737 ** we are at the beginning of next word 01738 ** which is a double byte character. 01739 */ 01740 if (ECIsDBCSLeadByte( ped, *pWordMaxSel)) 01741 break; 01742 01743 pWordMaxSel++; 01744 01745 if (ISDELIMETERA(*pWordMaxSel)) 01746 spaceLocated = TRUE; 01747 01748 if (*(pWordMaxSel - 1) == 0x0A) 01749 break; 01750 } 01751 01752 /* 01753 * label for fast return ( for Ansi ) 01754 */ 01755 FastReturnA: 01756 ECUnlock(ped); 01757 01758 if (pichMin) *pichMin = (ICH)(pWordMinSel - pText); 01759 if (pichMax) *pichMax = (ICH)(pWordMaxSel - pText); 01760 return; 01761 01762 } else { // !fAnsi 01763 LPWSTR pwText; 01764 LPWSTR pwWordMinSel; 01765 LPWSTR pwWordMaxSel; 01766 BOOL charLocated = FALSE; 01767 BOOL spaceLocated = FALSE; 01768 PWSTR pwPrevChar; 01769 01770 UserAssert(ped->cbChar == sizeof(WCHAR)); 01771 01772 if (ped->lpfnNextWord) { 01773 NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax); 01774 return; 01775 } 01776 01777 if (ped->pLpkEditCallout) { 01778 NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax); 01779 return; 01780 } 01781 01782 pwText = (LPWSTR)ECLock(ped); 01783 pwWordMinSel = pwWordMaxSel = pwText + ichStart; 01784 01785 /* 01786 * if fLeft: Move pWordMinSel to the left looking for the start of a word. 01787 * If we start at a space, we will include spaces in the selection as we 01788 * move left untill we find a nonspace character. At that point, we continue 01789 * looking left until we find a space. Thus, the selection will consist of 01790 * a word with its trailing spaces or, it will consist of any leading at the 01791 * beginning of a line of text. 01792 */ 01793 01794 /* 01795 * if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a 01796 * word. If the pWordMinSel points to a character, then we move left 01797 * looking for a space which will signify the start of the word. If 01798 * pWordMinSel points to a space, we look right till we come upon a 01799 * character. pMaxWord will look right starting at pMinWord looking for the 01800 * end of the word and its trailing spaces. 01801 */ 01802 01803 01804 if (fLeft || (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0D)) 01805 /* If we are moving left or if we are moving right and we are not on a 01806 * space or a CR (the start of a word), then we was look left for the 01807 * start of a word which is either a CR or a character. We do this by 01808 * looking left till we find a character (or if CR we stop), then we 01809 * continue looking left till we find a space or LF. 01810 */ { 01811 while (pwWordMinSel > pwText && ((!ISDELIMETERW(*(pwWordMinSel - 1)) && *(pwWordMinSel - 1) != 0x0A) || !charLocated)) { 01812 /* 01813 * Treat double byte character as a word ( in unicode pwWordMinSel loop ) 01814 */ 01815 pwPrevChar = pwWordMinSel - 1; 01816 /* 01817 ** we are looking right ( !fLeft ). 01818 ** 01819 ** if current character is a double width chararacter 01820 ** or previous character is a double width character, 01821 ** we are on the beggining of a word. 01822 */ 01823 if (!fLeft && (ISDELIMETERW( *pwPrevChar) || 01824 *pwPrevChar == 0x0A || 01825 UserIsFullWidth(CP_ACP,*pwWordMinSel) || 01826 UserIsFullWidth(CP_ACP,*pwPrevChar))) { 01827 /* 01828 * If we are looking for the start of the word right, then we 01829 * stop when we have found it. (needed in case charLocated is 01830 * still FALSE) 01831 */ 01832 break; 01833 } 01834 01835 if (UserIsFullWidth(CP_ACP,*pwPrevChar)) { 01836 /* 01837 ** Previous character is a double width character. 01838 ** 01839 ** if we are in a word ( charLocated == TRUE ) 01840 ** current position is the beginning of the word 01841 ** if we are not in a word ( charLocated == FALSE ) 01842 ** the previous character is what we looking for. 01843 */ 01844 if ( ! charLocated ) { 01845 pwWordMinSel = pwPrevChar; 01846 } 01847 break; 01848 } 01849 pwWordMinSel = pwPrevChar; 01850 01851 if (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0A) 01852 /* 01853 * We have found the last char in the word. Continue looking 01854 * backwards till we find the first char of the word 01855 */ { 01856 charLocated = TRUE; 01857 01858 /* 01859 * We will consider a CR the start of a word 01860 */ 01861 if (*pwWordMinSel == 0x0D) 01862 break; 01863 } 01864 } 01865 } else { 01866 01867 /* 01868 * We are moving right and we are in between words so we need to move 01869 * right till we find the start of a word (either a CR or a character. 01870 */ 01871 while ((ISDELIMETERW(*pwWordMinSel) || *pwWordMinSel == 0x0A) && pwWordMinSel < pwText + ped->cch) 01872 pwWordMinSel++; 01873 } 01874 01875 pwWordMaxSel = min((pwWordMinSel + 1), (pwText + ped->cch)); 01876 01877 /* 01878 ** If pwWordMinSel points a double width character AND 01879 ** pwWordMaxSel points non space 01880 ** then 01881 ** pwWordMaxSel points the beggining of next word. 01882 */ 01883 if (UserIsFullWidth(CP_ACP,*pwWordMinSel) && ! ISDELIMETERW(*pwWordMaxSel)) 01884 goto FastReturnW; 01885 if (*pwWordMinSel == 0x0D) { 01886 if (pwWordMinSel > pwText && *(pwWordMinSel - 1) == 0x0D) 01887 /* So that we can treat CRCRLF as one word also. */ 01888 pwWordMinSel--; 01889 else if (*(pwWordMinSel + 1) == 0x0D) 01890 /* Move MaxSel on to the LF */ 01891 pwWordMaxSel++; 01892 } 01893 01894 01895 01896 /* 01897 * Check if we have a one character word 01898 */ 01899 if (ISDELIMETERW(*pwWordMaxSel)) 01900 spaceLocated = TRUE; 01901 01902 /* 01903 * Move pwWordMaxSel to the right looking for the end of a word and its 01904 * trailing spaces. WordMaxSel stops on the first character of the next 01905 * word. Thus, we break either at a CR or at the first nonspace char after 01906 * a run of spaces or LFs. 01907 */ 01908 while ((pwWordMaxSel < pwText + ped->cch) && (!spaceLocated || (ISDELIMETERW(*pwWordMaxSel)))) { 01909 if (*pwWordMaxSel == 0x0D) 01910 break; 01911 01912 /* 01913 * treat double byte character as a word ( in unicode pwWordMaxSel loop ) 01914 */ 01915 /* 01916 ** if it's a double width character 01917 ** then we are at the beginning of 01918 ** the next word which is a double 01919 ** width character. 01920 */ 01921 if (UserIsFullWidth(CP_ACP,*pwWordMaxSel)) 01922 break; 01923 01924 pwWordMaxSel++; 01925 01926 if (ISDELIMETERW(*pwWordMaxSel)) 01927 spaceLocated = TRUE; 01928 01929 01930 if (*(pwWordMaxSel - 1) == 0x0A) 01931 break; 01932 } 01933 01934 /* 01935 * label for fast return ( for Unicode ) 01936 */ 01937 FastReturnW: 01938 ECUnlock(ped); 01939 01940 if (pichMin) *pichMin = (ICH)(pwWordMinSel - pwText); 01941 if (pichMax) *pichMax = (ICH)(pwWordMaxSel - pwText); 01942 return; 01943 } 01944 } 01945 01946 /***************************************************************************\ 01947 * 01948 * ECSaveUndo() - 01949 * 01950 * Saves old undo information into given buffer, and clears out info in 01951 * passed in undo buffer. If we're restoring, pundoFrom and pundoTo are 01952 * reversed. 01953 * 01954 \***************************************************************************/ 01955 void ECSaveUndo(PUNDO pundoFrom, PUNDO pundoTo, BOOL fClear) 01956 { 01957 /* 01958 * Save undo data 01959 */ 01960 RtlCopyMemory(pundoTo, pundoFrom, sizeof(UNDO)); 01961 01962 /* 01963 * Clear passed in undo buffer 01964 */ 01965 if (fClear) 01966 RtlZeroMemory(pundoFrom, sizeof(UNDO) ); 01967 } 01968 01969 /***************************************************************************\ 01970 * ECEmptyUndo AorW 01971 * 01972 * empties the undo buffer. 01973 * 01974 * History: 01975 \***************************************************************************/ 01976 01977 void ECEmptyUndo( 01978 PUNDO pundo ) 01979 { 01980 if (pundo->hDeletedText) 01981 UserGlobalFree(pundo->hDeletedText); 01982 01983 RtlZeroMemory(pundo, sizeof(UNDO) ); 01984 } 01985 01986 /***************************************************************************\ 01987 * 01988 * ECMergeUndoInsertInfo() - 01989 * 01990 * When an insert takes place, this function is called with the info about 01991 * the new insertion (the insertion point and the count of chars inserted); 01992 * This looks at the existing Undo info and merges the new new insert info 01993 * with it. 01994 * 01995 \***************************************************************************/ 01996 void ECMergeUndoInsertInfo(PUNDO pundo, ICH ichInsert, ICH cchInsert) \ 01997 { 01998 // 01999 // If undo buffer is empty, just insert the new info as UNDO_INSERT 02000 // 02001 if (pundo->undoType == UNDO_NONE) { 02002 pundo->undoType = UNDO_INSERT; 02003 pundo->ichInsStart = ichInsert; 02004 pundo->ichInsEnd = ichInsert+cchInsert; 02005 } else if (pundo->undoType & UNDO_INSERT) { 02006 // 02007 // If there's already some undo insert info, 02008 // try to merge the two. 02009 // 02010 if (pundo->ichInsEnd == ichInsert) // Check they are adjacent. 02011 pundo->ichInsEnd += cchInsert; // if so, just concatenate. 02012 else { 02013 // The new insert is not contiguous with the old one. 02014 UNDOINSERT: 02015 // 02016 // If there is some UNDO_DELETE info already here, check to see 02017 // if the new insert takes place at a point different from where 02018 // that deletion occurred. 02019 // 02020 if ((pundo->undoType & UNDO_DELETE) && (pundo->ichDeleted != ichInsert)) { 02021 // 02022 // User is inserting into a different point; So, let us 02023 // forget any UNDO_DELETE info; 02024 // 02025 if (pundo->hDeletedText) 02026 UserGlobalFree(pundo->hDeletedText); 02027 02028 pundo->hDeletedText = NULL; 02029 pundo->ichDeleted = 0xFFFFFFFF; 02030 pundo->undoType &= ~UNDO_DELETE; 02031 } 02032 02033 // Since the old insert and new insert are not adjacent, let us 02034 // forget everything about the old insert and keep just the new 02035 // insert info as the UNDO_INSERT. 02036 pundo->ichInsStart = ichInsert; 02037 pundo->ichInsEnd = ichInsert + cchInsert; 02038 pundo->undoType |= UNDO_INSERT; 02039 } 02040 } else if (pundo->undoType == UNDO_DELETE) { 02041 // If there is some Delete Info already present go and handle it. 02042 goto UNDOINSERT; 02043 } 02044 } 02045 02046 02047 /***************************************************************************\ 02048 * ECInsertText AorW 02049 * 02050 * Adds cch characters from lpText into the ped->hText starting at 02051 * ped->ichCaret. Returns TRUE if successful else FALSE. Updates 02052 * ped->cchAlloc and ped->cch properly if additional memory was allocated or 02053 * if characters were actually added. Updates ped->ichCaret to be at the end 02054 * of the inserted text. min and maxsel are equal to ichcaret. 02055 * 02056 * History: 02057 \***************************************************************************/ 02058 02059 BOOL ECInsertText( 02060 PED ped, 02061 LPSTR lpText, 02062 ICH* pcchInsert) 02063 { 02064 PSTR pedText; 02065 PSTR pTextBuff; 02066 LONG style; 02067 HANDLE hTextCopy; 02068 DWORD allocamt; 02069 02070 // 02071 // If the last byte (lpText[cchInsert - 1]) is a DBCS leading byte 02072 // we need to adjust it. 02073 // 02074 *pcchInsert = ECAdjustIch(ped, lpText, *pcchInsert); 02075 02076 if (!*pcchInsert) 02077 return TRUE; 02078 02079 /* 02080 * Do we already have enough memory?? 02081 */ 02082 if (*pcchInsert >= (ped->cchAlloc - ped->cch)) { 02083 02084 /* 02085 * Allocate what we need plus a little extra. Return FALSE if we are 02086 * unsuccessful. 02087 */ 02088 allocamt = (ped->cch + *pcchInsert) * ped->cbChar; 02089 allocamt += CCHALLOCEXTRA; 02090 02091 // if (!ped->fSingle) { 02092 hTextCopy = LOCALREALLOC(ped->hText, allocamt, LHND, ped->hInstance, &lpText); 02093 if (hTextCopy) { 02094 ped->hText = hTextCopy; 02095 } else { 02096 return FALSE; 02097 } 02098 // } else { 02099 // if (!LocalReallocSafe(ped->hText, allocamt, LHND, pped)) 02100 // return FALSE; 02101 // } 02102 02103 ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar; 02104 } 02105 02106 02107 /* 02108 * Ok, we got the memory. Now copy the text into the structure 02109 */ 02110 pedText = ECLock(ped); 02111 02112 if (ped->pLpkEditCallout) { 02113 HDC hdc; 02114 INT iResult; 02115 02116 hdc = ECGetEditDC (ped, TRUE); 02117 iResult = ped->pLpkEditCallout->EditVerifyText (ped, hdc, pedText, ped->ichCaret, lpText, *pcchInsert); 02118 ECReleaseEditDC (ped, hdc, TRUE); 02119 02120 if (iResult == 0) { 02121 ECUnlock (ped); 02122 return TRUE; 02123 } 02124 } 02125 02126 /* 02127 * Get a pointer to the place where text is to be inserted 02128 */ 02129 pTextBuff = pedText + ped->ichCaret * ped->cbChar; 02130 02131 if (ped->ichCaret != ped->cch) { 02132 02133 /* 02134 * We are inserting text into the middle. We have to shift text to the 02135 * right before inserting new text. 02136 */ 02137 memmove(pTextBuff + *pcchInsert * ped->cbChar, pTextBuff, (ped->cch-ped->ichCaret) * ped->cbChar); 02138 } 02139 02140 /* 02141 * Make a copy of the text being inserted in the edit buffer. 02142 * Use this copy for doing UPPERCASE/LOWERCASE ANSI/OEM conversions 02143 * Fix for Bug #3406 -- 01/29/91 -- SANKAR -- 02144 */ 02145 memmove(pTextBuff, lpText, *pcchInsert * ped->cbChar); 02146 ped->cch += *pcchInsert; 02147 02148 /* 02149 * Get the control's style 02150 */ 02151 style = ped->pwnd->style; 02152 02153 /* 02154 * Do the Upper/Lower conversion 02155 */ 02156 if (style & ES_LOWERCASE) { 02157 if (ped->fAnsi) 02158 CharLowerBuffA((LPSTR)pTextBuff, *pcchInsert); 02159 else 02160 CharLowerBuffW((LPWSTR)pTextBuff, *pcchInsert); 02161 } else { 02162 if (style & ES_UPPERCASE) { 02163 if (ped->fAnsi) { 02164 CharUpperBuffA(pTextBuff, *pcchInsert); 02165 } else { 02166 CharUpperBuffW((LPWSTR)pTextBuff, *pcchInsert); 02167 } 02168 } 02169 } 02170 02171 /* 02172 * Do the OEM conversion 02173 */ 02174 if ((style & ES_OEMCONVERT) && 02175 // For backward compatibility with NT4, we don't perform OEM conversion 02176 // for older apps if the system locale is FarEast. 02177 // 02178 (!IS_DBCS_ENABLED() || GETAPPVER() >= VER50 || GetOEMCP() != GetACP())) { 02179 02180 ICH i; 02181 02182 if (ped->fAnsi) { 02183 for (i = 0; i < *pcchInsert; i++) { 02184 // 02185 // We don't need to call CharToOemBuff etc. if the character 02186 // is a double byte character. And, calling ECIsDBCSLeadByte is 02187 // faster and less complicated because we don't have to deal 02188 // with the 2 byte dbcs cases. 02189 // 02190 if (IS_DBCS_ENABLED() && ECIsDBCSLeadByte(ped, *(lpText+i))) { 02191 i++; 02192 continue; 02193 } 02194 02195 // 02196 // Windows Bug (Whistler) 35289 02197 // greek has funny rules for casing, so we need to check for it. 02198 // for nashville we should be doing something more appropriate 02199 // but for now, leave as Win95 golden 02200 // 02201 if (ped->charSet != GREEK_CHARSET && IsCharLowerA(*(pTextBuff + i))) { 02202 CharUpperBuffA(pTextBuff + i, 1); 02203 CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1); 02204 OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1); 02205 CharLowerBuffA(pTextBuff + i, 1); 02206 } else { 02207 CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1); 02208 OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1); 02209 } 02210 } 02211 } else { 02212 // 02213 // Because 'ch' may become DBCS, and have a space for NULL. 02214 // 02215 UCHAR ch[4]; 02216 LPWSTR lpTextW = (LPWSTR)pTextBuff; 02217 02218 for (i = 0; i < *pcchInsert; i++) { 02219 if (*(lpTextW + i) == UNICODE_CARRIAGERETURN || 02220 *(lpTextW + i) == UNICODE_LINEFEED || 02221 *(lpTextW + i) == UNICODE_TAB) { 02222 continue; 02223 } 02224 // 02225 // Windows Bug (Whistler) 35289 02226 // greek has funny rules for casing, so we need to check for it. 02227 // for nashville we should be doing something more appropriate 02228 // but for now, leave as Win95 golden 02229 // 02230 if (ped->charSet != GREEK_CHARSET && IsCharLowerW(*(lpTextW + i))) { 02231 CharUpperBuffW(lpTextW + i, 1); 02232 *(LPDWORD)ch = 0; // make sure the null-terminate. 02233 CharToOemBuffW(lpTextW + i, ch, 1); 02234 // 02235 // We assume any SBCS/DBCS character will converted 02236 // to 1 Unicode char, Otherwise, we may overwrite 02237 // next character... 02238 // 02239 OemToCharBuffW(ch, lpTextW + i, strlen(ch)); 02240 CharLowerBuffW(lpTextW + i, 1); 02241 } else { 02242 *(LPDWORD)ch = 0; // make sure the null-terminate. 02243 CharToOemBuffW(lpTextW + i, ch, 1); 02244 // 02245 // We assume any SBCS/DBCS character will converted 02246 // to 1 Unicode char, Otherwise, we may overwrite 02247 // next character... 02248 // 02249 OemToCharBuffW(ch, lpTextW + i, strlen(ch)); 02250 } 02251 } 02252 } 02253 } 02254 02255 /* Adjust UNDO fields so that we can undo this insert... */ 02256 ECMergeUndoInsertInfo(Pundo(ped), ped->ichCaret, *pcchInsert); 02257 02258 ped->ichCaret += *pcchInsert; 02259 02260 if (ped->pLpkEditCallout) { 02261 HDC hdc; 02262 02263 hdc = ECGetEditDC (ped, TRUE); 02264 ped->ichCaret = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichCaret); 02265 ECReleaseEditDC (ped, hdc, TRUE); 02266 } 02267 02268 ped->ichMinSel = ped->ichMaxSel = ped->ichCaret; 02269 02270 ECUnlock(ped); 02271 02272 /* 02273 * Set dirty bit 02274 */ 02275 ped->fDirty = TRUE; 02276 02277 return TRUE; 02278 } 02279 02280 /***************************************************************************\ 02281 * ECDeleteText AorW 02282 * 02283 * Deletes the text between ped->ichMinSel and ped->ichMaxSel. The 02284 * character at ichMaxSel is not deleted. But the character at ichMinSel is 02285 * deleted. ped->cch is updated properly and memory is deallocated if enough 02286 * text is removed. ped->ichMinSel, ped->ichMaxSel, and ped->ichCaret are set 02287 * to point to the original ped->ichMinSel. Returns the number of characters 02288 * deleted. 02289 * 02290 * History: 02291 \***************************************************************************/ 02292 02293 ICH ECDeleteText( 02294 PED ped) 02295 { 02296 PSTR pedText; 02297 ICH cchDelete; 02298 LPSTR lpDeleteSaveBuffer; 02299 HANDLE hDeletedText; 02300 DWORD bufferOffset; 02301 02302 cchDelete = ped->ichMaxSel - ped->ichMinSel; 02303 02304 if (!cchDelete) 02305 return (0); 02306 02307 /* 02308 * Ok, now lets delete the text. 02309 */ 02310 pedText = ECLock(ped); 02311 02312 /* 02313 * Adjust UNDO fields so that we can undo this delete... 02314 */ 02315 if (ped->undoType == UNDO_NONE) { 02316 UNDODELETEFROMSCRATCH: 02317 if (ped->hDeletedText = UserGlobalAlloc(GPTR, (LONG)((cchDelete+1)*ped->cbChar))) { 02318 ped->undoType = UNDO_DELETE; 02319 ped->ichDeleted = ped->ichMinSel; 02320 ped->cchDeleted = cchDelete; 02321 lpDeleteSaveBuffer = ped->hDeletedText; 02322 RtlCopyMemory(lpDeleteSaveBuffer, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar); 02323 lpDeleteSaveBuffer[cchDelete*ped->cbChar] = 0; 02324 } 02325 } else if (ped->undoType & UNDO_INSERT) { 02326 UNDODELETE: 02327 ECEmptyUndo(Pundo(ped)); 02328 02329 ped->ichInsStart = ped->ichInsEnd = 0xFFFFFFFF; 02330 ped->ichDeleted = 0xFFFFFFFF; 02331 ped->cchDeleted = 0; 02332 goto UNDODELETEFROMSCRATCH; 02333 } else if (ped->undoType == UNDO_DELETE) { 02334 if (ped->ichDeleted == ped->ichMaxSel) { 02335 02336 /* 02337 * Copy deleted text to front of undo buffer 02338 */ 02339 hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND); 02340 if (!hDeletedText) 02341 goto UNDODELETE; 02342 bufferOffset = 0; 02343 ped->ichDeleted = ped->ichMinSel; 02344 } else if (ped->ichDeleted == ped->ichMinSel) { 02345 02346 /* 02347 * Copy deleted text to end of undo buffer 02348 */ 02349 hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND); 02350 if (!hDeletedText) 02351 goto UNDODELETE; 02352 bufferOffset = ped->cchDeleted*ped->cbChar; 02353 } else { 02354 02355 /* 02356 * Clear the current UNDO delete and add the new one since 02357 the deletes aren't contiguous. 02358 */ 02359 goto UNDODELETE; 02360 } 02361 02362 ped->hDeletedText = hDeletedText; 02363 lpDeleteSaveBuffer = (LPSTR)hDeletedText; 02364 if (!bufferOffset) { 02365 02366 /* 02367 * Move text in delete buffer up so that we can insert the next 02368 * text at the head of the buffer. 02369 */ 02370 RtlMoveMemory(lpDeleteSaveBuffer + cchDelete*ped->cbChar, lpDeleteSaveBuffer, 02371 ped->cchDeleted*ped->cbChar); 02372 } 02373 RtlCopyMemory(lpDeleteSaveBuffer + bufferOffset, pedText + ped->ichMinSel*ped->cbChar, 02374 cchDelete*ped->cbChar); 02375 02376 lpDeleteSaveBuffer[(ped->cchDeleted + cchDelete)*ped->cbChar] = 0; 02377 ped->cchDeleted += cchDelete; 02378 } 02379 02380 if (ped->ichMaxSel != ped->cch) { 02381 02382 /* 02383 * We are deleting text from the middle of the buffer so we have to 02384 shift text to the left. 02385 */ 02386 RtlMoveMemory(pedText + ped->ichMinSel*ped->cbChar, pedText + ped->ichMaxSel*ped->cbChar, 02387 (ped->cch - ped->ichMaxSel)*ped->cbChar); 02388 } 02389 02390 if (ped->cchAlloc - ped->cch > CCHALLOCEXTRA) { 02391 02392 /* 02393 * Free some memory since we deleted a lot 02394 */ 02395 LOCALREALLOC(ped->hText, (DWORD)(ped->cch + (CCHALLOCEXTRA / 2))*ped->cbChar, LHND, ped->hInstance, NULL); 02396 ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar; 02397 } 02398 02399 ped->cch -= cchDelete; 02400 02401 if (ped->pLpkEditCallout) { 02402 HDC hdc; 02403 02404 hdc = ECGetEditDC (ped, TRUE); 02405 ped->ichMinSel = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichMinSel); 02406 ECReleaseEditDC (ped, hdc, TRUE); 02407 } 02408 02409 ped->ichCaret = ped->ichMaxSel = ped->ichMinSel; 02410 02411 ECUnlock(ped); 02412 02413 /* 02414 * Set dirty bit 02415 */ 02416 ped->fDirty = TRUE; 02417 02418 return (cchDelete); 02419 } 02420 02421 /***************************************************************************\ 02422 * ECNotifyParent AorW 02423 * 02424 * Sends the notification code to the parent of the edit control 02425 * 02426 * History: 02427 \***************************************************************************/ 02428 02429 void ECNotifyParent( 02430 PED ped, 02431 int notificationCode) 02432 { 02433 /* 02434 * wParam is NotificationCode (hiword) and WindowID (loword) 02435 * lParam is HWND of control sending the message 02436 * Windows 95 checks for hwndParent != NULL before sending the message, but 02437 * this is surely rare, and SendMessage NULL hwnd does nowt anyway (IanJa) 02438 */ 02439 SendMessage(ped->hwndParent, WM_COMMAND, 02440 (DWORD)MAKELONG(PTR_TO_ID(ped->pwnd->spmenu), notificationCode), 02441 (LPARAM)ped->hwnd); 02442 } 02443 02444 /***************************************************************************\ 02445 * 02446 * ECSetEditClip() AorW 02447 * 02448 * Sets the clip rect for the hdc to the formatting rectangle intersected 02449 * with the client area. 02450 * 02451 \***************************************************************************/ 02452 void ECSetEditClip(PED ped, HDC hdc, BOOL fLeftMargin) 02453 { 02454 RECT rcClient; 02455 RECT rcClip; 02456 02457 CopyRect(&rcClip, &ped->rcFmt); 02458 02459 if (ped->pLpkEditCallout) { 02460 // Complex script handling chooses whether to write margins later 02461 rcClip.left -= ped->wLeftMargin; 02462 rcClip.right += ped->wRightMargin; 02463 } else { 02464 if (fLeftMargin) /* Should we consider the left margin? */ 02465 rcClip.left -= ped->wLeftMargin; 02466 if (ped->fWrap) /* Should we consider the right margin? */ 02467 rcClip.right += ped->wRightMargin; 02468 } 02469 02470 /* Set clip rectangle to rectClient intersect rectClip */ 02471 /* We must clip for single line edits also. -- B#1360 */ 02472 _GetClientRect(ped->pwnd, &rcClient); 02473 if (ped->fFlatBorder) 02474 InflateRect(&rcClient, -SYSMET(CXBORDER), -SYSMET(CYBORDER)); 02475 02476 IntersectRect(&rcClient, &rcClient, &rcClip); 02477 IntersectClipRect(hdc,rcClient.left, rcClient.top, 02478 rcClient.right, rcClient.bottom); 02479 } 02480 02481 /***************************************************************************\ 02482 * ECGetEditDC AorW 02483 * 02484 * Hides the caret, gets the DC for the edit control, and clips to 02485 * the rcFmt rectangle specified for the edit control and sets the proper 02486 * font. If fFastDC, just select the proper font but don't bother about clip 02487 * regions or hiding the caret. 02488 * 02489 * History: 02490 \***************************************************************************/ 02491 02492 HDC ECGetEditDC( 02493 PED ped, 02494 BOOL fFastDC ) 02495 { 02496 HDC hdc; 02497 02498 if (!fFastDC) 02499 NtUserHideCaret(ped->hwnd); 02500 02501 if ( hdc = NtUserGetDC(ped->hwnd) ) { 02502 ECSetEditClip(ped, hdc, (BOOL)(ped->xOffset == 0)); 02503 02504 /* 02505 * Select the proper font for this edit control's dc. 02506 */ 02507 if (ped->hFont) 02508 SelectObject(hdc, ped->hFont); 02509 } 02510 02511 return hdc; 02512 } 02513 02514 /***************************************************************************\ 02515 * ECReleaseEditDC AorW 02516 * 02517 * Releases the DC (hdc) for the edit control and shows the caret. 02518 * If fFastDC, just select the proper font but don't bother about showing the 02519 * caret. 02520 * 02521 * History: 02522 \***************************************************************************/ 02523 02524 void ECReleaseEditDC( 02525 PED ped, 02526 HDC hdc, 02527 BOOL fFastDC) 02528 { 02529 /* 02530 * Restoring font not necessary 02531 */ 02532 02533 ReleaseDC(ped->hwnd, hdc); 02534 02535 if (!fFastDC) 02536 NtUserShowCaret(ped->hwnd); 02537 } 02538 02539 /***************************************************************************\ 02540 * 02541 * ECResetTextInfo() AorW 02542 * 02543 * Handles a global change to the text by resetting text offsets, emptying 02544 * the undo buffer, and rebuilding the lines 02545 * 02546 \***************************************************************************/ 02547 void ECResetTextInfo(PED ped) 02548 { 02549 // 02550 // Reset caret, selections, scrolling, and dirty information. 02551 // 02552 ped->iCaretLine = ped->ichCaret = 0; 02553 ped->ichMinSel = ped->ichMaxSel = 0; 02554 ped->xOffset = ped->ichScreenStart = 0; 02555 ped->fDirty = FALSE; 02556 02557 ECEmptyUndo(Pundo(ped)); 02558 02559 if (ped->fSingle) { 02560 if (!ped->listboxHwnd) 02561 ECNotifyParent(ped, EN_UPDATE); 02562 } else { 02563 #ifdef BOGUS 02564 // B#14640 02565 // We don't want to strip soft breaks or anything else from text 02566 // that was passed in by the caller. - karlst. 02567 MLStripCrCrLf(ped); 02568 #endif 02569 MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL); 02570 } 02571 02572 if (_IsWindowVisible(ped->pwnd)) { 02573 BOOL fErase; 02574 02575 if (ped->fSingle) 02576 fErase = FALSE; 02577 else 02578 fErase = ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines); 02579 02580 // Always redraw whether or not the insert was successful. We might 02581 // have NULL text. Paint() will check the redraw flag for us. 02582 ECInvalidateClient(ped, fErase); 02583 02584 // BACKWARD COMPAT HACK: RAID expects the text to have been updated, 02585 // so we have to do an UpdateWindow here. It moves an edit control 02586 // around with fRedraw == FALSE, so it'll never get the paint message 02587 // with the control in the right place. 02588 if (!ped->fWin31Compat) 02589 UpdateWindow(ped->hwnd); 02590 } 02591 02592 if (ped->fSingle && !ped->listboxHwnd) 02593 ECNotifyParent(ped, EN_CHANGE); 02594 02595 if (FWINABLE()) { 02596 NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, HW(ped->pwnd), OBJID_CLIENT, 02597 INDEXID_CONTAINER); 02598 } 02599 } 02600 02601 /***************************************************************************\ 02602 * ECSetText AorW 02603 * 02604 * Copies the null terminated text in lpstr to the ped. Notifies the 02605 * parent if there isn't enough memory. Sets the minsel, maxsel, and caret to 02606 * the beginning of the inserted text. Returns TRUE if successful else FALSE 02607 * if no memory (and notifies the parent). 02608 * 02609 * History: 02610 \***************************************************************************/ 02611 02612 BOOL ECSetText( 02613 PED ped, 02614 LPSTR lpstr) 02615 { 02616 ICH cchLength; 02617 ICH cchSave = ped->cch; 02618 ICH ichCaretSave = ped->ichCaret; 02619 HWND hwndSave = ped->hwnd; 02620 HANDLE hText; 02621 02622 ped->cch = ped->ichCaret = 0; 02623 02624 ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar; 02625 if (!lpstr) { 02626 hText = LOCALREALLOC(ped->hText, CCHALLOCEXTRA*ped->cbChar, LHND, ped->hInstance, &lpstr); 02627 if (hText != NULL) { 02628 ped->hText = hText; 02629 } else { 02630 return FALSE; 02631 } 02632 } else { 02633 cchLength = StringLength(lpstr, ped->fAnsi); 02634 02635 #ifdef NEVER 02636 // win3.1 does limit single line edit controls to 32K (minus 3) but NT doesn't 02637 02638 if (ped->fSingle) { 02639 /* 02640 * Limit single line edit controls to 32K 02641 */ 02642 cchLength = min(cchLength, (ICH)(0x7FFD/ped->cbChar)); 02643 } 02644 #endif 02645 02646 /* 02647 * Add the text 02648 */ 02649 if (cchLength && !ECInsertText(ped, lpstr, &cchLength)) { 02650 02651 /* 02652 * Restore original state and notify parent we ran out of memory. 02653 */ 02654 ped->cch = cchSave; 02655 ped->ichCaret = ichCaretSave; 02656 ECNotifyParent(ped, EN_ERRSPACE); 02657 return FALSE; 02658 } 02659 } 02660 02661 ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar; 02662 02663 if (IsWindow(hwndSave)) 02664 ECResetTextInfo(ped); 02665 02666 return TRUE; 02667 } 02668 02669 /***************************************************************************\ 02670 * 02671 * ECInvalidateClient() 02672 * 02673 * Invalidates client of edit field. For old 3.x guys with borders, 02674 * we draw it ourself (compatibility). So we don't want to invalidate 02675 * the border or we'll get flicker. 02676 * 02677 \***************************************************************************/ 02678 02679 void ECInvalidateClient(PED ped, BOOL fErase) 02680 { 02681 if (ped->fFlatBorder) { 02682 RECT rcT; 02683 02684 _GetClientRect(ped->pwnd, &rcT); 02685 InflateRect(&rcT, -SYSMET(CXBORDER), 02686 -SYSMET(CYBORDER)); 02687 NtUserInvalidateRect(ped->hwnd, &rcT, fErase); 02688 } else { 02689 NtUserInvalidateRect(ped->hwnd, NULL, fErase); 02690 } 02691 } 02692 02693 02694 /***************************************************************************\ 02695 * ECCopy AorW 02696 * 02697 * Copies the text between ichMinSel and ichMaxSel to the clipboard. 02698 * Returns the number of characters copied. 02699 * 02700 * History: 02701 \***************************************************************************/ 02702 02703 ICH ECCopy( 02704 PED ped) 02705 { 02706 HANDLE hData; 02707 char *pchSel; 02708 char FAR *lpchClip; 02709 ICH cbData; 02710 02711 /* 02712 * Don't allow copies from password style controls 02713 */ 02714 if (ped->charPasswordChar) { 02715 NtUserMessageBeep(0); 02716 return 0; 02717 } 02718 02719 cbData = (ped->ichMaxSel - ped->ichMinSel) * ped->cbChar; 02720 02721 if (!cbData) 02722 return 0; 02723 02724 if (!OpenClipboard(ped->hwnd)) 02725 return 0; 02726 02727 NtUserEmptyClipboard(); 02728 02729 /* 02730 * If we just called EmptyClipboard in the context of a 16 bit 02731 * app then we also have to tell WOW to nix its 16 handle copy of 02732 * clipboard data. WOW does its own clipboard caching because 02733 * some 16 bit apps use clipboard data even after the clipboard 02734 * has been emptied. See the note in the server code. 02735 * 02736 * Note: this is the only place where EmptyClipboard is called 02737 * for a 16 bit app not going through WOW. If we added others 02738 * we might want to move this into EmptyClipboard and have two 02739 * versions. 02740 */ 02741 if (GetClientInfo()->CI_flags & CI_16BIT) { 02742 pfnWowEmptyClipBoard(); 02743 } 02744 02745 02746 /* 02747 * +1 for the terminating NULL 02748 */ 02749 if (!(hData = UserGlobalAlloc(LHND, (LONG)(cbData + ped->cbChar)))) { 02750 NtUserCloseClipboard(); 02751 return (0); 02752 } 02753 02754 USERGLOBALLOCK(hData, lpchClip); 02755 UserAssert(lpchClip); 02756 pchSel = ECLock(ped); 02757 pchSel = pchSel + (ped->ichMinSel * ped->cbChar); 02758 02759 RtlCopyMemory(lpchClip, pchSel, cbData); 02760 02761 if (ped->fAnsi) 02762 *(lpchClip + cbData) = 0; 02763 else 02764 *(LPWSTR)(lpchClip + cbData) = (WCHAR)0; 02765 02766 ECUnlock(ped); 02767 USERGLOBALUNLOCK(hData); 02768 02769 SetClipboardData( ped->fAnsi ? CF_TEXT : CF_UNICODETEXT, hData); 02770 02771 NtUserCloseClipboard(); 02772 02773 return (cbData); 02774 } 02775 02776 02777 02778 /***************************************************************************\ 02779 * EditWndProcA 02780 * 02781 * Always receives Ansi messages and translates them if appropriate to unicode 02782 * depending on the PED type 02783 * 02784 * 02785 \***************************************************************************/ 02786 02787 LRESULT EditWndProcA( 02788 HWND hwnd, 02789 UINT message, 02790 WPARAM wParam, 02791 LPARAM lParam) 02792 { 02793 PWND pwnd; 02794 02795 if ((pwnd = ValidateHwnd(hwnd)) == NULL) 02796 return 0; 02797 02798 /* 02799 * If the control is not interested in this message, 02800 * pass it to DefWindowProc. 02801 */ 02802 if (!FWINDOWMSG(message, FNID_EDIT)) 02803 return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE); 02804 02805 return EditWndProcWorker(pwnd, message, wParam, lParam, TRUE); 02806 } 02807 02808 LRESULT EditWndProcW( 02809 HWND hwnd, 02810 UINT message, 02811 WPARAM wParam, 02812 LPARAM lParam) 02813 { 02814 PWND pwnd; 02815 02816 if ((pwnd = ValidateHwnd(hwnd)) == NULL) 02817 return 0; 02818 02819 /* 02820 * If the control is not interested in this message, 02821 * pass it to DefWindowProc. 02822 */ 02823 if (!FWINDOWMSG(message, FNID_EDIT)) { 02824 return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE); 02825 } 02826 02827 return EditWndProcWorker(pwnd, message, wParam, lParam, FALSE); 02828 } 02829 02830 02831 LRESULT EditWndProcWorker( 02832 PWND pwnd, 02833 UINT message, 02834 WPARAM wParam, 02835 LPARAM lParam, 02836 DWORD fAnsi) 02837 { 02838 PED ped; 02839 HWND hwnd = HWq(pwnd); 02840 static BOOL fInit = TRUE; 02841 02842 VALIDATECLASSANDSIZE(pwnd, FNID_EDIT); 02843 INITCONTROLLOOKASIDE(&EditLookaside, ED, pwnd, 4); 02844 02845 /* 02846 * Get the ped for the given window now since we will use it a lot in 02847 * various handlers. This was stored using SetWindowLong(hwnd,0,hped) when 02848 * we initially created the edit control. 02849 */ 02850 ped = ((PEDITWND)pwnd)->ped; 02851 02852 /* 02853 * Make sure the ANSI flag is set correctly. 02854 */ 02855 if (!ped->fInitialized) { 02856 ped->fInitialized = TRUE; 02857 ped->fAnsi = TestWF(pwnd, WFANSICREATOR) ? TRUE : FALSE; 02858 } 02859 02860 /* 02861 * We just call the regular EditWndProc if the ped is not created, the 02862 * incoming message type already matches the PED type or the message 02863 * does not need any translation. 02864 */ 02865 if (ped->fAnsi == fAnsi || 02866 (message >= WM_USER) || 02867 !MessageTable[message].bThunkMessage) { 02868 return EditWndProc(pwnd, message, wParam, lParam); 02869 } 02870 02871 return CsSendMessage(hwnd, message, wParam, lParam, 02872 fAnsi ? (ULONG_PTR)EditWndProcW : (ULONG_PTR)EditWndProcA, 02873 FNID_CALLWINDOWPROC, fAnsi); 02874 } 02875 02876 /***************************************************************************\ 02877 * EditWndProc 02878 * 02879 * Class procedure for all edit controls. 02880 * Dispatches all messages to the appropriate handlers which are named 02881 * as follows: 02882 * SL (single line) prefixes all single line edit control procedures while 02883 * ML (multi line) prefixes all multi- line edit controls. 02884 * EC (edit control) prefixes all common handlers. 02885 * 02886 * The EditWndProc only handles messages common to both single and multi 02887 * line edit controls. Messages which are handled differently between 02888 * single and multi are sent to SLEditWndProc or MLEditWndProc. 02889 * 02890 * Top level procedures are EditWndPoc, SLEditWndProc, and MLEditWndProc. 02891 * SL*Handler or ML*Handler or EC*Handler procs are called to handle 02892 * the various messages. Support procedures are prefixed with SL ML or 02893 * EC depending on which code they support. They are never called 02894 * directly and most assumptions/effects are documented in the effects 02895 * clause. 02896 * 02897 * WARNING: If you add a message here, add it to gawEditWndProc[] in 02898 * kernel\server.c too, otherwise EditWndProcA/W will send it straight to 02899 * DefWindowProcWorker 02900 * 02901 * History: 02902 \***************************************************************************/ 02903 02904 LRESULT EditWndProc( 02905 PWND pwnd, 02906 UINT message, 02907 WPARAM wParam, 02908 LPARAM lParam) 02909 { 02910 HWND hwnd = HWq(pwnd); 02911 LRESULT lreturn; 02912 PED ped; 02913 02914 /* 02915 * Get the ped for the given window now since we will use it a lot in 02916 * various handlers. This was stored using SetWindowLong(hwnd,0,hped) when 02917 * we initially created the edit control. 02918 */ 02919 ped = ((PEDITWND)pwnd)->ped; 02920 02921 /* 02922 * Dispatch the various messages we can receive 02923 */ 02924 lreturn = 1L; 02925 switch (message) { 02926 02927 /* 02928 * Messages which are handled the same way for both single and multi line 02929 * edit controls. 02930 */ 02931 case WM_KEYDOWN: 02932 // LPK handling of Ctrl/LShift, Ctrl/RShift 02933 if (ped && ped->pLpkEditCallout && ped->fAllowRTL) { 02934 02935 ped->fSwapRoOnUp = FALSE; // Any keydown cancels a ctrl/shift reading order change 02936 02937 switch (wParam) { 02938 case VK_SHIFT: 02939 if ((GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) { 02940 // Left shift or right shift pressed while control held down 02941 // Check that alt (VK_MENU) isn't down to avoid false firing on AltGr which equals Ctrl+Alt. 02942 if (MapVirtualKey((LONG)lParam>>16&0xff, 3) == VK_LSHIFT) { 02943 // User wants left to right reading order 02944 ped->fSwapRoOnUp = (ped->fRtoLReading) || (ped->format & ES_RIGHT) ; 02945 ped->fLShift = TRUE; 02946 } else { 02947 // User wants right to left reading order 02948 ped->fSwapRoOnUp = (!ped->fRtoLReading) || (ped->format & ES_RIGHT); 02949 ped->fLShift = FALSE; 02950 } 02951 } 02952 break; 02953 02954 case VK_LEFT: 02955 if (ped->fRtoLReading) { 02956 wParam = VK_RIGHT; 02957 } 02958 break; 02959 02960 case VK_RIGHT: 02961 if (ped->fRtoLReading) { 02962 wParam = VK_LEFT; 02963 } 02964 break; 02965 } 02966 } 02967 goto HandleEditMsg; 02968 02969 case WM_KEYUP: 02970 if (ped && ped->pLpkEditCallout && ped->fAllowRTL && ped->fSwapRoOnUp) { 02971 02972 BOOL fReadingOrder; 02973 // Complete reading order change detected earlier during keydown 02974 02975 ped->fSwapRoOnUp = FALSE; 02976 fReadingOrder = ped->fRtoLReading; 02977 02978 // Remove any overriding ES_CENTRE or ES_RIGHT format from dwStyle 02979 SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~ES_FMTMASK); 02980 02981 if (ped->fLShift) { 02982 // Set Left to Right reading order and right scrollbar in EX_STYLE 02983 SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) 02984 & ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR)); 02985 02986 // Edit control is LTR now, then notify the parent. 02987 ECNotifyParent(ped, EN_ALIGN_LTR_EC); 02988 // ? Select a keyboard layout appropriate to LTR operation 02989 } else { 02990 // Set Right to Left reading order, right alignment and left scrollbar 02991 SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) 02992 | WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR); 02993 02994 // Edit control is RTL now, then notify the parent. 02995 ECNotifyParent(ped, EN_ALIGN_RTL_EC); 02996 // ? Select a keyboard layout appropriate to RTL operation 02997 } 02998 02999 // If reading order didn't change, so we are sure the alignment changed and the edit window didn't invalidate yet. 03000 if (fReadingOrder == (BOOL) ped->fRtoLReading) { 03001 ECInvalidateClient(ped, TRUE); 03002 } 03003 } 03004 goto HandleEditMsg; 03005 03006 case WM_INPUTLANGCHANGE: 03007 if (ped) { 03008 // EC_INSERT_COMPOSITION_CHAR : WM_INPUTLANGCHANGE - call ECInitInsert() 03009 HKL hkl = THREAD_HKL(); 03010 03011 ECInitInsert(ped, hkl); 03012 03013 if (ped->fInReconversion) { 03014 ECInOutReconversionMode(ped, FALSE); 03015 } 03016 03017 // 03018 // Font and caret position might be changed while 03019 // another keyboard layout is active. Set those 03020 // if the edit control has the focus. 03021 // 03022 if (ped->fFocus && fpImmIsIME(hkl)) { 03023 POINT pt; 03024 03025 ECImmSetCompositionFont(ped); 03026 NtUserGetCaretPos(&pt); 03027 ECImmSetCompositionWindow(ped, pt.x, pt.y); 03028 } 03029 } 03030 03031 goto HandleEditMsg; 03032 03033 case WM_COPY: 03034 03035 /* 03036 * wParam - not used 03037 * lParam - not used 03038 */ 03039 lreturn = (LONG)ECCopy(ped); 03040 break; 03041 03042 case WM_CUT: 03043 /* 03044 * 03045 * wParamLo -- unused 03046 * lParam -- unused 03047 */ 03048 ECCutText(ped); 03049 return 0; 03050 03051 case WM_CLEAR: 03052 /* 03053 * wParamLo -- unused 03054 * lParam -- unused 03055 */ 03056 ECClearText(ped); 03057 return 0; 03058 03059 case WM_ENABLE: 03060 03061 /* 03062 * wParam - nonzero if window is enabled else disable window if 0. 03063 * lParam - not used 03064 */ 03065 lreturn = (LONG)(ped->fDisabled = !((BOOL)wParam)); 03066 ECInvalidateClient(ped, TRUE); 03067 break; 03068 03069 case WM_SYSCHAR: 03070 // 03071 // wParamLo -- key value 03072 // lParam -- unused 03073 // 03074 03075 // 03076 // If this is a WM_SYSCHAR message generated by the UNDO 03077 // keystroke we want to EAT IT 03078 // 03079 if ((lParam & SYS_ALTERNATE) && ((WORD)wParam == VK_BACK)) 03080 return TRUE; 03081 else { 03082 return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi); 03083 } 03084 break; 03085 03086 case EM_GETLINECOUNT: 03087 03088 /* 03089 * wParam - not used 03090 lParam - not used 03091 */ 03092 lreturn = (LONG)ped->cLines; 03093 break; 03094 03095 case EM_GETMODIFY: 03096 03097 /* 03098 * wParam - not used 03099 lParam - not used 03100 */ 03101 03102 /* 03103 * Gets the state of the modify flag for this edit control. 03104 */ 03105 lreturn = (LONG)ped->fDirty; 03106 break; 03107 03108 case EM_SETMODIFY: 03109 03110 /* 03111 * wParam - specifies the new value for the modify flag 03112 lParam - not used 03113 */ 03114 03115 /* 03116 * Sets the state of the modify flag for this edit control. 03117 */ 03118 ped->fDirty = (wParam != 0); 03119 break; 03120 03121 case EM_GETRECT: 03122 03123 /* 03124 * wParam - not used 03125 lParam - pointer to a RECT data structure that gets the dimensions. 03126 */ 03127 03128 /* 03129 * Copies the rcFmt rect to *lpRect. 03130 */ 03131 CopyRect((LPRECT)lParam, (LPRECT)&ped->rcFmt); 03132 lreturn = (LONG)TRUE; 03133 break; 03134 03135 case WM_GETFONT: 03136 03137 /* 03138 * wParam - not used 03139 lParam - not used 03140 */ 03141 lreturn = (LRESULT)ped->hFont; 03142 break; 03143 03144 case WM_SETFONT: 03145 03146 /* 03147 * wParam - handle to the font 03148 lParam - redraw if true else don't 03149 */ 03150 ECSetFont(ped, (HANDLE)wParam, (BOOL)LOWORD(lParam)); 03151 break; 03152 03153 case WM_GETTEXT: 03154 03155 /* 03156 * wParam - max number of _bytes_ (not characters) to copy 03157 * lParam - buffer to copy text to. Text is 0 terminated. 03158 */ 03159 lreturn = (LRESULT)ECGetText(ped, (ICH)wParam, (LPSTR)lParam, TRUE); 03160 break; 03161 03162 case WM_SETTEXT: 03163 // 03164 // wParamLo -- unused 03165 // lParam -- LPSTR, null-terminated, with new text. 03166 // 03167 lreturn = (LRESULT)ECSetText(ped, (LPSTR)lParam); 03168 break; 03169 03170 case WM_GETTEXTLENGTH: 03171 03172 /* 03173 * Return count of CHARs!!! 03174 */ 03175 lreturn = (LONG)ped->cch; 03176 break; 03177 03178 case WM_NCDESTROY: 03179 case WM_FINALDESTROY: 03180 03181 /* 03182 * wParam - not used 03183 lParam - not used 03184 */ 03185 ECNcDestroyHandler(pwnd, ped); 03186 return 0; 03187 03188 /* 03189 * Most apps (i.e. everyone but Quicken) don't pass on the rbutton 03190 * messages when they do something with 'em inside of subclassed 03191 * edit fields. As such, we keep track of whether we saw the 03192 * down before the up. If we don't see the up, then DefWindowProc 03193 * won't generate the context menu message, so no big deal. If 03194 * we didn't see the down, then don't let WM_CONTEXTMENU do 03195 * anything. 03196 * 03197 * We also might want to not generate WM_CONTEXTMENUs for old 03198 * apps when the mouse is captured. 03199 */ 03200 03201 case WM_RBUTTONDOWN: 03202 ped->fSawRButtonDown = TRUE; 03203 goto HandleEditMsg; 03204 03205 case WM_RBUTTONUP: 03206 if (ped->fSawRButtonDown) { 03207 ped->fSawRButtonDown = FALSE; 03208 if (!ped->fInReconversion) { 03209 goto HandleEditMsg; 03210 } 03211 } 03212 // Don't pass this on to DWP so WM_CONTEXTMENU isn't generated. 03213 return 0; 03214 03215 case WM_CONTEXTMENU: { 03216 POINT pt ; 03217 int nHit = FindNCHit(pwnd, (LONG)lParam); 03218 if ((nHit == HTVSCROLL) || (nHit == HTHSCROLL)) { 03219 return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi); 03220 } 03221 POINTSTOPOINT(pt, lParam); 03222 if (!TestWF(pwnd, WFOLDUI) && ECIsAncestorActive(hwnd)) 03223 ECMenu(hwnd, ped, &pt); 03224 } 03225 return 0; 03226 03227 case EM_CANUNDO: 03228 03229 /* 03230 * wParam - not used 03231 lParam - not used 03232 */ 03233 lreturn = (LONG)(ped->undoType != UNDO_NONE); 03234 break; 03235 03236 case EM_EMPTYUNDOBUFFER: 03237 03238 /* 03239 * wParam - not used 03240 lParam - not used 03241 */ 03242 ECEmptyUndo(Pundo(ped)); 03243 break; 03244 03245 case EM_GETMARGINS: 03246 // 03247 // wParam -- unused 03248 // lParam -- unused 03249 // 03250 return(MAKELONG(ped->wLeftMargin, ped->wRightMargin)); 03251 03252 case EM_SETMARGINS: 03253 // 03254 // wParam -- EC_ margin flags 03255 // lParam -- LOWORD is left, HIWORD is right margin 03256 // 03257 ECSetMargin(ped, (UINT)wParam, (DWORD)lParam, TRUE); 03258 return 0; 03259 03260 case EM_GETSEL: 03261 03262 /* 03263 * Gets the selection range for the given edit control. The 03264 * starting position is in the low order word. It contains the position 03265 * of the first nonselected character after the end of the selection in 03266 * the high order word. 03267 */ 03268 if ((PDWORD)wParam != NULL) { 03269 *((PDWORD)wParam) = ped->ichMinSel; 03270 } 03271 if ((PDWORD)lParam != NULL) { 03272 *((PDWORD)lParam) = ped->ichMaxSel; 03273 } 03274 lreturn = MAKELONG(ped->ichMinSel,ped->ichMaxSel); 03275 break; 03276 03277 case EM_GETLIMITTEXT: 03278 // 03279 // wParamLo -- unused 03280 // lParam -- unused 03281 // 03282 return(ped->cchTextMax); 03283 03284 case EM_SETLIMITTEXT: /* Renamed from EM_LIMITTEXT in Chicago */ 03285 /* 03286 * wParam - max number of CHARACTERS that can be entered 03287 * lParam - not used 03288 */ 03289 03290 /* 03291 * Specifies the maximum number of characters of text the user may 03292 * enter. If maxLength is 0, we may enter MAXINT number of CHARACTERS. 03293 */ 03294 if (ped->fSingle) { 03295 if (wParam) { 03296 wParam = min(0x7FFFFFFEu, wParam); 03297 } else { 03298 wParam = 0x7FFFFFFEu; 03299 } 03300 } 03301 03302 if (wParam) { 03303 ped->cchTextMax = (ICH)wParam; 03304 } else { 03305 ped->cchTextMax = 0xFFFFFFFFu; 03306 } 03307 break; 03308 03309 case EM_POSFROMCHAR: 03310 // 03311 // Validate that char index is within text range 03312 // 03313 if (wParam >= ped->cch) { 03314 return(-1L); 03315 } 03316 goto HandleEditMsg; 03317 03318 case EM_CHARFROMPOS: { 03319 // Validate that point is within client of edit field 03320 RECT rc; 03321 POINT pt; 03322 03323 POINTSTOPOINT(pt, lParam); 03324 _GetClientRect(pwnd, &rc); 03325 if (!PtInRect(&rc, pt)) { 03326 return(-1L); 03327 } 03328 goto HandleEditMsg; 03329 } 03330 03331 case EM_SETPASSWORDCHAR: 03332 03333 /* 03334 * wParam - sepecifies the new char to display instead of the 03335 * real text. if null, display the real text. 03336 */ 03337 ECSetPasswordChar(ped, (UINT)wParam); 03338 break; 03339 03340 case EM_GETPASSWORDCHAR: 03341 lreturn = (DWORD)ped->charPasswordChar; 03342 break; 03343 03344 case EM_SETREADONLY: 03345 03346 /* 03347 * wParam - state to set read only flag to 03348 */ 03349 ped->fReadOnly = (wParam != 0); 03350 if (wParam) 03351 SetWindowState(pwnd, EFREADONLY); 03352 else 03353 ClearWindowState(pwnd, EFREADONLY); 03354 lreturn = 1L; 03355 03356 ECEnableDisableIME( ped ); 03357 // We need to redraw the edit field so that the background color 03358 // changes. Read-only edits are drawn in CTLCOLOR_STATIC while 03359 // others are drawn with CTLCOLOR_EDIT. 03360 ECInvalidateClient(ped, TRUE); 03361 break; 03362 03363 case EM_SETWORDBREAKPROC: 03364 03365 /* 03366 * wParam - unused 03367 * lParam - FARPROC address of an app supplied call back function 03368 */ 03369 ped->lpfnNextWord = (EDITWORDBREAKPROCA)lParam; 03370 break; 03371 03372 case EM_GETWORDBREAKPROC: 03373 lreturn = (LRESULT)ped->lpfnNextWord; 03374 break; 03375 03376 // IME 03377 case EM_GETIMESTATUS: 03378 // wParam == sub command 03379 switch (wParam) { 03380 case EMSIS_COMPOSITIONSTRING: 03381 return ped->wImeStatus; 03382 #if 0 // memphis 03383 case EMSIS_GETLBBIT: 03384 return (DWORD)ped->bLBBit; 03385 #endif 03386 } 03387 break; 03388 03389 case EM_SETIMESTATUS: 03390 // wParam == sub command 03391 switch (wParam) { 03392 case EMSIS_COMPOSITIONSTRING: 03393 ped->wImeStatus = (WORD)lParam; 03394 } 03395 break; 03396 03397 03398 case WM_NCCREATE: 03399 lreturn = ECNcCreate(ped, pwnd, (LPCREATESTRUCT)lParam); 03400 break; 03401 03402 case WM_LBUTTONDOWN: 03403 // 03404 // B#3623 03405 // Don't set focus to edit field if it is within an inactive, 03406 // captioned child. 03407 // We might want to version switch this... I haven't found 03408 // any problems by not, but you never know... 03409 // 03410 if (ECIsAncestorActive(hwnd)) { 03411 /* 03412 * Reconversion support: quit reconversion if left button is clicked. 03413 * Otherwise, if the current KL is Korean, finailize the composition string. 03414 */ 03415 if (ped->fInReconversion || ped->fKorea) { 03416 BOOLEAN fReconversion = (BOOLEAN)ped->fInReconversion; 03417 DWORD dwIndex = fReconversion ? CPS_CANCEL : CPS_COMPLETE; 03418 HIMC hImc; 03419 03420 ped->fReplaceCompChr = FALSE; 03421 03422 hImc = fpImmGetContext(ped->hwnd); 03423 if (hImc) { 03424 fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, dwIndex, 0); 03425 fpImmReleaseContext(ped->hwnd, hImc); 03426 } 03427 03428 if (fReconversion) { 03429 ECInOutReconversionMode(ped, FALSE); 03430 } 03431 03432 ECSetCaretHandler(ped); 03433 } 03434 03435 goto HandleEditMsg; 03436 } 03437 break; 03438 03439 case WM_MOUSEMOVE: 03440 // 03441 // We only care about mouse messages when mouse is down. 03442 // 03443 if (ped->fMouseDown) 03444 goto HandleEditMsg; 03445 break; 03446 03447 case WM_IME_SETCONTEXT: 03448 // 03449 // If ped->fInsertCompChr is TRUE, that means we will do 03450 // all the composition character drawing by ourself. 03451 // 03452 if ( ped->fInsertCompChr ) { 03453 lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; 03454 } 03455 03456 if ( wParam ) { 03457 03458 PINPUTCONTEXT pInputContext; 03459 HIMC hImc; 03460 03461 hImc = fpImmGetContext( hwnd ); 03462 if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) { 03463 pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS; 03464 fpImmUnlockIMC( hImc ); 03465 } 03466 if (GetClientInfo()->CI_flags & CI_16BIT) { 03467 fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L); 03468 } 03469 fpImmReleaseContext( hwnd, hImc ); 03470 } 03471 return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi); 03472 03473 case WM_IME_ENDCOMPOSITION: 03474 ECInOutReconversionMode(ped, FALSE); 03475 03476 if (ped->fReplaceCompChr) { 03477 ICH ich; 03478 HDC hdc; 03479 // 03480 // we have a DBCS character to be replaced. 03481 // let's delete it before inserting the new one. 03482 // 03483 ich = (ped->fAnsi) ? 2 : 1; 03484 ped->fReplaceCompChr = FALSE; 03485 ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch); 03486 ped->ichMinSel = ped->ichCaret; 03487 if (ped->fSingle) { 03488 if (ECDeleteText( ped ) > 0) { 03489 // 03490 // Update the display 03491 // 03492 ECNotifyParent(ped, EN_UPDATE); 03493 hdc = ECGetEditDC(ped, FALSE); 03494 SLDrawText(ped, hdc, 0); 03495 ECReleaseEditDC(ped, hdc, FALSE); 03496 // 03497 // Tell parent our text contents changed. 03498 // 03499 ECNotifyParent(ped, EN_CHANGE); 03500 } 03501 } 03502 else { 03503 MLDeleteText(ped); 03504 } 03505 03506 ECSetCaretHandler( ped ); 03507 } 03508 return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi); 03509 03510 case WM_IME_STARTCOMPOSITION: 03511 if ( ped->fInsertCompChr ) { 03512 // 03513 // BUG BUG 03514 // 03515 // sending WM_IME_xxxCOMPOSITION will let 03516 // IME draw composition window. IME should 03517 // not do that since we cleared 03518 // ISC_SHOWUICOMPOSITIONWINDOW bit when 03519 // we got WM_IME_SETCONTEXT message. 03520 // 03521 // Korean IME should be fixed in the future. 03522 // 03523 break; 03524 03525 } else { 03526 return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi); 03527 } 03528 03529 // simple composition character support for FE IME. 03530 case WM_IME_COMPOSITION: 03531 return ECImeComposition(ped, wParam, lParam); 03532 03533 case WM_KILLFOCUS: 03534 // 03535 // when focus is removed from the window, 03536 // composition character should be finalized 03537 // 03538 if (ped && fpImmIsIME(THREAD_HKL())) { 03539 HIMC hImc = fpImmGetContext(hwnd); 03540 03541 if (hImc != NULL_HIMC) { 03542 if (ped->fReplaceCompChr || (ped->wImeStatus & EIMES_COMPLETECOMPSTRKILLFOCUS)) { 03543 // If the composition string to be determined upon kill focus, 03544 // do it now. 03545 fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); 03546 } else if (ped->fInReconversion) { 03547 // If the composition string it not to be determined, 03548 // and if we're in reconversion mode, cancel reconversion now. 03549 fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 03550 } 03551 03552 // Get out from reconversion mode 03553 if (ped->fInReconversion) { 03554 ECInOutReconversionMode(ped, FALSE); 03555 } 03556 03557 fpImmReleaseContext(hwnd, hImc); 03558 } 03559 } 03560 goto HandleEditMsg; 03561 break; 03562 03563 case WM_SETFOCUS: 03564 if (ped && !ped->fFocus) { 03565 HKL hkl = THREAD_HKL(); 03566 03567 if (fpImmIsIME(hkl)) { 03568 HIMC hImc; 03569 03570 hImc = fpImmGetContext(hwnd); 03571 if (hImc) { 03572 LPINPUTCONTEXT lpImc; 03573 03574 if (ped->wImeStatus & EIMES_CANCELCOMPSTRINFOCUS) { 03575 // cancel when in-focus 03576 fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); 03577 } 03578 03579 ECImmSetCompositionFont(ped); 03580 03581 if ((lpImc = fpImmLockIMC(hImc)) != NULL) { 03582 03583 // We presume the CompForm will reset to CFS_DEFAULT, 03584 // when the edit control loses Focus. 03585 // IMEWndProc32 will call ImmSetCompositionWindow with 03586 // CFS_DEFAULT, when it receive WM_IME_SETCONTEXT. 03587 lpImc->fdw31Compat |= F31COMPAT_ECSETCFS; 03588 03589 fpImmUnlockIMC(hImc); 03590 } 03591 fpImmReleaseContext(hwnd, hImc); 03592 } 03593 03594 // 03595 // force to set IME composition window when 03596 // first getting focus. 03597 // 03598 ped->ptScreenBounding.x = -1; 03599 ped->ptScreenBounding.y = -1; 03600 } 03601 ECInitInsert(ped, hkl); 03602 } 03603 goto HandleEditMsg; 03604 break; 03605 03606 case WM_IME_REQUEST: 03607 // simple ImeRequest Handler 03608 return EcImeRequestHandler(ped, wParam, lParam); 03609 03610 case WM_CREATE: 03611 if (ped) 03612 ECEnableDisableIME(ped); 03613 goto HandleEditMsg; 03614 break; 03615 03616 default: 03617 HandleEditMsg: 03618 /* (picked up from NT40FE SP3) 03619 * HACK ALERT: We may receive messages before the PED has been 03620 * allocated (eg: WM_GETMINMAXINFO is sent before WM_NCCREATE) 03621 * so we must test ped before dreferencing. 03622 */ 03623 if (ped != NULL) { 03624 if (ped->fSingle) { 03625 lreturn = SLEditWndProc(hwnd, ped, message, wParam, lParam); 03626 } else { 03627 lreturn = MLEditWndProc(hwnd, ped, message, wParam, lParam); 03628 } 03629 } 03630 } 03631 03632 return lreturn; 03633 } 03634 03635 /***************************************************************************\ 03636 * ECFindXORblks 03637 * 03638 * This finds the XOR of lpOldBlk and lpNewBlk and return s resulting blocks 03639 * through the lpBlk1 and lpBlk2; This could result in a single block or 03640 * at the maximum two blocks; 03641 * If a resulting block is empty, then it's StPos field has -1. 03642 * NOTE: 03643 * When called from MultiLine edit control, StPos and EndPos fields of 03644 * these blocks have the Starting line and Ending line of the block; 03645 * When called from SingleLine edit control, StPos and EndPos fields 03646 * of these blocks have the character index of starting position and 03647 * ending position of the block. 03648 * 03649 * History: 03650 \***************************************************************************/ 03651 03652 void ECFindXORblks( 03653 LPBLOCK lpOldBlk, 03654 LPBLOCK lpNewBlk, 03655 LPBLOCK lpBlk1, 03656 LPBLOCK lpBlk2) 03657 { 03658 if (lpOldBlk->StPos >= lpNewBlk->StPos) { 03659 lpBlk1->StPos = lpNewBlk->StPos; 03660 lpBlk1->EndPos = min(lpOldBlk->StPos, lpNewBlk->EndPos); 03661 } else { 03662 lpBlk1->StPos = lpOldBlk->StPos; 03663 lpBlk1->EndPos = min(lpNewBlk->StPos, lpOldBlk->EndPos); 03664 } 03665 03666 if (lpOldBlk->EndPos <= lpNewBlk->EndPos) { 03667 lpBlk2->StPos = max(lpOldBlk->EndPos, lpNewBlk->StPos); 03668 lpBlk2->EndPos = lpNewBlk->EndPos; 03669 } else { 03670 lpBlk2->StPos = max(lpNewBlk->EndPos, lpOldBlk->StPos); 03671 lpBlk2->EndPos = lpOldBlk->EndPos; 03672 } 03673 } 03674 03675 /***************************************************************************\ 03676 * ECCalcChangeSelection 03677 * 03678 * This function finds the XOR between two selection blocks(OldBlk and NewBlk) 03679 * and return s the resulting areas thro the same parameters; If the XOR of 03680 * both the blocks is empty, then this return s FALSE; Otherwise TRUE. 03681 * 03682 * NOTE: 03683 * When called from MultiLine edit control, StPos and EndPos fields of 03684 * these blocks have the Starting line and Ending line of the block; 03685 * When called from SingleLine edit control, StPos and EndPos fields 03686 * of these blocks have the character index of starting position and 03687 * ending position of the block. 03688 * 03689 * History: 03690 \***************************************************************************/ 03691 03692 BOOL ECCalcChangeSelection( 03693 PED ped, 03694 ICH ichOldMinSel, 03695 ICH ichOldMaxSel, 03696 LPBLOCK OldBlk, 03697 LPBLOCK NewBlk) 03698 { 03699 BLOCK Blk[2]; 03700 int iBlkCount = 0; 03701 03702 Blk[0].StPos = Blk[0].EndPos = Blk[1].StPos = Blk[1].EndPos = 0xFFFFFFFF; 03703 03704 /* 03705 * Check if the Old selection block existed 03706 */ 03707 if (ichOldMinSel != ichOldMaxSel) { 03708 03709 /* 03710 * Yes! Old block existed. 03711 */ 03712 Blk[0].StPos = OldBlk->StPos; 03713 Blk[0].EndPos = OldBlk->EndPos; 03714 iBlkCount++; 03715 } 03716 03717 /* 03718 * Check if the new Selection block exists 03719 */ 03720 if (ped->ichMinSel != ped->ichMaxSel) { 03721 03722 /* 03723 * Yes! New block exists 03724 */ 03725 Blk[1].StPos = NewBlk->StPos; 03726 Blk[1].EndPos = NewBlk->EndPos; 03727 iBlkCount++; 03728 } 03729 03730 /* 03731 * If both the blocks exist find the XOR of them 03732 */ 03733 if (iBlkCount == 2) { 03734 03735 /* 03736 * Check if both blocks start at the same character position 03737 */ 03738 if (ichOldMinSel == ped->ichMinSel) { 03739 03740 /* 03741 * Check if they end at the same character position 03742 */ 03743 if (ichOldMaxSel == ped->ichMaxSel) 03744 return FALSE; /* Nothing changes */ 03745 03746 Blk[0].StPos = min(NewBlk -> EndPos, OldBlk -> EndPos); 03747 Blk[0].EndPos = max(NewBlk -> EndPos, OldBlk -> EndPos); 03748 Blk[1].StPos = 0xFFFFFFFF; 03749 } else { 03750 if (ichOldMaxSel == ped->ichMaxSel) { 03751 Blk[0].StPos = min(NewBlk->StPos, OldBlk->StPos); 03752 Blk[0].EndPos = max(NewBlk->StPos, OldBlk->StPos); 03753 Blk[1].StPos = 0xFFFFFFFF; 03754 } else { 03755 ECFindXORblks(OldBlk, NewBlk, &Blk[0], &Blk[1]); 03756 } 03757 } 03758 } 03759 03760 RtlCopyMemory(OldBlk, &Blk[0], sizeof(BLOCK)); 03761 RtlCopyMemory(NewBlk, &Blk[1], sizeof(BLOCK)); 03762 03763 return TRUE; /* Yup , There is something to paint */ 03764 } 03765 03766 03767 /***************************************************************************\ 03768 * ECGetControlBrush 03769 * 03770 * Client side optimization replacement for NtUserGetControlBrush 03771 * 03772 * message is one of the WM_CTLCOLOR* messages. 03773 * 03774 \***************************************************************************/ 03775 03776 HBRUSH ECGetControlBrush( 03777 PED ped, 03778 HDC hdc, 03779 LONG message) 03780 { 03781 PWND pwndSend; 03782 PWND pwndEdit; 03783 03784 pwndEdit = ValidateHwnd(ped->hwnd); 03785 03786 if (pwndEdit == (PWND)NULL) 03787 return (HBRUSH)0; 03788 03789 if ((pwndSend = (TestwndPopup(pwndEdit) ? pwndEdit->spwndOwner : pwndEdit->spwndParent)) == NULL) 03790 pwndSend = pwndEdit; 03791 else 03792 pwndSend = REBASEPTR(pwndEdit, pwndSend); 03793 03794 UserAssert(pwndSend); 03795 03796 if (PtiCurrent() != GETPTI(pwndSend)) { 03797 return (HBRUSH)DefWindowProcWorker(pwndSend, message, 03798 (WPARAM)hdc, (LPARAM)pwndEdit, ped->fAnsi); 03799 } 03800 03801 /* 03802 * By using the correct A/W call we avoid a c/s transition 03803 * on this SendMessage(). 03804 */ 03805 return (HBRUSH)SendMessageWorker(pwndSend, message, (WPARAM)hdc, 03806 (LPARAM)ped->hwnd, ped->fAnsi); 03807 } 03808 03809 UINT WINAPI QueryFontAssocStatus(void); 03810 UINT fFontAssocStatus = 0xffff; 03811 03812 /***************************************************************************\ 03813 * ECGetDBCSVector( PED ped, BYTE CharSet ) 03814 * 03815 * This function sets DBCS Vector for specified character set and sets 03816 * ped->fDBCS flag if needed. 03817 * 03818 * History: 18-Jun-1996 Hideyuki Nagase 03819 \***************************************************************************/ 03820 int ECGetDBCSVector(PED ped, HDC hdc, BYTE CharSet) 03821 { 03822 BOOL bDBCSCodePage = FALSE; 03823 /* 03824 * if DEFAUT_CHARSET was passed, we will convert that to Shell charset.. 03825 */ 03826 if (CharSet == DEFAULT_CHARSET) { 03827 CharSet = (BYTE)GetTextCharset(hdc); 03828 03829 /* 03830 * if CharSet is still DEFAULT_CHARSET, it means gdi has some problem.. 03831 * then just return default.. we get charset from CP_ACP.. 03832 */ 03833 if (CharSet == DEFAULT_CHARSET) { 03834 CharSet = (BYTE)GetACPCharSet(); 03835 } 03836 } 03837 03838 switch (CharSet) { 03839 case SHIFTJIS_CHARSET: 03840 case HANGEUL_CHARSET: 03841 case CHINESEBIG5_CHARSET: 03842 case GB2312_CHARSET: 03843 bDBCSCodePage = TRUE; 03844 break; 03845 03846 case ANSI_CHARSET: // 0 03847 case SYMBOL_CHARSET: // 2 03848 case OEM_CHARSET: // 255 03849 if (fFontAssocStatus == 0xffff) 03850 fFontAssocStatus = QueryFontAssocStatus(); 03851 03852 if ((((CharSet + 2) & 0xf) & fFontAssocStatus)) { 03853 bDBCSCodePage = TRUE; 03854 /* 03855 * Bug 117558, etc. 03856 * Try to get a meaningful character set for associated font. 03857 */ 03858 CharSet = (BYTE)GetACPCharSet(); 03859 } else { 03860 bDBCSCodePage = FALSE; 03861 } 03862 break; 03863 03864 default: 03865 bDBCSCodePage = FALSE; 03866 } 03867 03868 if (bDBCSCodePage) { 03869 CHARSETINFO CharsetInfo; 03870 DWORD CodePage; 03871 CPINFO CPInfo; 03872 int lbIX; 03873 03874 if (TranslateCharsetInfo((DWORD *)CharSet, &CharsetInfo, TCI_SRCCHARSET)) { 03875 CodePage = CharsetInfo.ciACP; 03876 } else { 03877 CodePage = CP_ACP; 03878 } 03879 03880 GetCPInfo(CodePage, &CPInfo); 03881 for (lbIX=0 ; CPInfo.LeadByte[lbIX] != 0 ; lbIX+=2) { 03882 ped->DBCSVector[lbIX ] = CPInfo.LeadByte[lbIX]; 03883 ped->DBCSVector[lbIX+1] = CPInfo.LeadByte[lbIX+1]; 03884 } 03885 ped->DBCSVector[lbIX ] = 0x0; 03886 ped->DBCSVector[lbIX+1] = 0x0; 03887 } else { 03888 ped->DBCSVector[0] = 0x0; 03889 ped->DBCSVector[1] = 0x0; 03890 } 03891 03892 // 03893 // Final check: if the font supports DBCS glyphs 03894 // 03895 // If we've got a font with DBCS glyphs, let's mark PED so. 03896 // But since the font's primary charset is the one other than FE, 03897 // we can only support UNICODE Edit control. 03898 // 03899 // a) GDI performs A/W conversion for ANSI apps based on the primary 03900 // character set in hDC, so it will break anyway. 03901 // b) ANSI applications are only supported on their native system locales: 03902 // GetACPCharSet() is expected to return a FE code page. 03903 // c) ANSI Edit control requires DBCSVector, which cannot be 03904 // initialized without a FE code page. 03905 // 03906 if (!ped->fAnsi) { 03907 FONTSIGNATURE fontSig; 03908 03909 GetTextCharsetInfo(hdc, &fontSig, 0); 03910 if (fontSig.fsCsb[0] & FAREAST_CHARSET_BITS) { 03911 bDBCSCodePage = TRUE; 03912 // Since this is UNICODE, we're not 03913 } 03914 } 03915 03916 return bDBCSCodePage; 03917 } 03918 03919 /***************************************************************************\ 03920 * LPSTR ECAnsiNext( ped, lpCurrent ) 03921 * 03922 * This function advances string pointer for Edit Control use only. 03923 * 03924 * History: 03925 \***************************************************************************/ 03926 LPSTR ECAnsiNext(PED ped, LPSTR lpCurrent) 03927 { 03928 return lpCurrent+((ECIsDBCSLeadByte(ped,*lpCurrent)==TRUE) ? 2 : 1); 03929 } 03930 03931 /***************************************************************************\ 03932 * LPSTR ECAnsiPrev( ped, lpBase, lpStr ) 03933 * 03934 * This function decrements string pointer for Edit Control use only. 03935 * 03936 * History: 03937 \***************************************************************************/ 03938 LPSTR ECAnsiPrev(PED ped, LPSTR lpBase, LPSTR lpStr ) 03939 { 03940 LPSTR lpCurrent = lpStr -1; 03941 03942 if (!ped->fDBCS) 03943 return lpCurrent; // just return ( lpStr - 1 ) 03944 03945 if (lpBase >= lpCurrent) 03946 return lpBase; 03947 03948 if (ECIsDBCSLeadByte(ped, *lpCurrent)) // this check makes things faster 03949 return (lpCurrent - 1); // 92/04/04 takaok 03950 03951 do { 03952 lpCurrent--; 03953 if (!ECIsDBCSLeadByte(ped, *lpCurrent)) { 03954 lpCurrent++; 03955 break; 03956 } 03957 } while(lpCurrent != lpBase); 03958 03959 return lpStr - (((lpStr - lpCurrent) & 1) ? 1 : 2); 03960 } 03961 03962 /***************************************************************************\ 03963 * ICH ECNextIch( ped, pText, ichCurrent ) 03964 * 03965 * This function advances string pointer for Edit Control use only. 03966 * 03967 * History: 03968 \***************************************************************************/ 03969 ICH ECNextIch( PED ped, LPSTR pStart, ICH ichCurrent ) 03970 { 03971 if (!ped->fDBCS || !ped->fAnsi) { 03972 03973 return (ichCurrent + 1); 03974 03975 } else { 03976 03977 ICH ichRet; 03978 LPSTR pText; 03979 03980 if (pStart) 03981 pText = pStart + ichCurrent; 03982 else 03983 pText = (LPSTR)ECLock(ped) + ichCurrent; 03984 03985 ichRet = ichCurrent + ( ECIsDBCSLeadByte(ped, *pText) ? 2 : 1 ); 03986 03987 if (!pStart) 03988 ECUnlock(ped); 03989 03990 return (ichRet); 03991 } 03992 } 03993 03994 /***************************************************************************\ 03995 * ICH ECPrevIch( ped, LPSTR pStart, ICH ichCurrent ) 03996 * 03997 * This function decrements string pointer for Edit Control use only. 03998 * 03999 * History: 04000 \***************************************************************************/ 04001 ICH ECPrevIch( PED ped, LPSTR pStart, ICH ichCurrent ) 04002 { 04003 LPSTR lpCurrent; 04004 LPSTR lpStr; 04005 LPSTR lpBase; 04006 04007 #ifdef SURROGATE 04008 // Handle Unicode surrogates pairs when CSLPK is loaded 04009 if (ped->fAnsi || !ped->pLpkEditCallout) // if no surrogate processing required 04010 #endif 04011 if (!ped->fDBCS || !ped->fAnsi) 04012 if ( ichCurrent ) 04013 return (ichCurrent - 1); 04014 else 04015 return (ichCurrent); 04016 04017 if (ichCurrent <= 1) 04018 return 0; 04019 04020 if (pStart) 04021 lpBase = pStart; 04022 else 04023 lpBase = ECLock(ped); 04024 04025 #ifdef SURROGATE 04026 04027 // Handle characters represented by multiple codepoints 04028 04029 if (ped->fAnsi) { 04030 04031 // ANSI PrevIch with DBCS support 04032 #endif 04033 04034 lpStr = lpBase + ichCurrent; 04035 lpCurrent = lpStr - 1; 04036 if (ECIsDBCSLeadByte(ped,*lpCurrent)) { 04037 if (!pStart) 04038 ECUnlock(ped); 04039 return (ichCurrent - 2); 04040 } 04041 04042 do { 04043 lpCurrent--; 04044 if (!ECIsDBCSLeadByte(ped, *lpCurrent)) { 04045 lpCurrent++; 04046 break; 04047 } 04048 } while(lpCurrent != lpBase); 04049 04050 if (!pStart) 04051 ECUnlock(ped); 04052 return (ichCurrent - (((lpStr - lpCurrent) & 1) ? 1 : 2)); 04053 04054 #ifdef SURROGATE 04055 04056 } else { 04057 04058 // Unicode PrevIch with surrogate pair support 04059 04060 ichCurrent--; 04061 04062 if ( (((WCHAR*)lpBase)[ichCurrent] & 0xFC00) == 0xDC00 04063 && (((WCHAR*)lpBase)[ichCurrent-1] & 0xFC00) == 0xD800) { 04064 04065 ichCurrent--; 04066 } 04067 04068 if (!pStart) 04069 ECUnlock(ped); 04070 04071 return ichCurrent; 04072 } 04073 #endif 04074 } 04075 04076 /***************************************************************************\ 04077 * BOOL ECIsDBCSLeadByte( PED ped, BYTE cch ) 04078 * 04079 * IsDBCSLeadByte for Edit Control use only. 04080 * 04081 * History: 18-Jun-1996 Hideyuki Nagase 04082 \***************************************************************************/ 04083 BOOL ECIsDBCSLeadByte(PED ped, BYTE cch) 04084 { 04085 int i; 04086 04087 if (!ped->fDBCS || !ped->fAnsi) 04088 return (FALSE); 04089 04090 for (i = 0; ped->DBCSVector[i]; i += 2) { 04091 if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch)) 04092 return (TRUE); 04093 } 04094 04095 return (FALSE); 04096 } 04097 04098 /***************************************************************************\ 04099 * int DBCSCombine(HWND hwnd, int ch) 04100 * 04101 * Assemble two WM_CHAR messages to single DBCS character. 04102 * If program detects first byte of DBCS character in WM_CHAR message, 04103 * it calls this function to obtain second WM_CHAR message from queue. 04104 * finally this routine assembles first byte and second byte into single 04105 * DBCS character. 04106 * 04107 * History: 04108 \***************************************************************************/ 04109 WORD DbcsCombine(HWND hwnd, WORD ch) 04110 { 04111 MSG msg; 04112 int i = 10; /* loop counter to avoid the infinite loop */ 04113 04114 while (!PeekMessageA(&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) { 04115 if (--i == 0) 04116 return 0; 04117 Sleep(1); 04118 } 04119 04120 return (WORD)ch | ((WORD)(msg.wParam) << 8); 04121 } 04122 04123 /***************************************************************************\ 04124 * ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch ) 04125 * 04126 * This function adjusts a current pointer correctly. If a current 04127 * pointer is lying between DBCS first byte and second byte, this 04128 * function adjusts a current pointer to a first byte of DBCS position 04129 * by decrement once. 04130 * 04131 * History: 04132 \***************************************************************************/ 04133 ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch ) 04134 { 04135 ICH newch = ch; 04136 04137 if (!ped->fAnsi || !ped->fDBCS || newch == 0) 04138 return ( ch ); 04139 04140 if (!ECIsDBCSLeadByte(ped,lpstr[--newch])) 04141 return ( ch ); // previous char is SBCS 04142 while(1) { 04143 if (!ECIsDBCSLeadByte(ped,lpstr[newch])) { 04144 newch++; 04145 break; 04146 } 04147 if (newch) 04148 newch--; 04149 else 04150 break; 04151 } 04152 return ((ch - newch) & 1) ? ch-1 : ch; 04153 } 04154 04155 /***************************************************************************\ 04156 * ICH ECAdjustIchNext( PED ped, LPSTR lpstr, ICH ch ) 04157 * 04158 * History: 04159 * 19.Jun.1996 Hideyuki Nagase [hideyukn] - Port from Win95-FarEast version 04160 \***************************************************************************/ 04161 04162 ICH FAR PASCAL ECAdjustIchNext(PED ped, LPSTR lpstr, ICH ch) 04163 { 04164 ICH ichNew = ECAdjustIch(ped,lpstr,ch); 04165 LPSTR lpnew = lpstr+ichNew; 04166 04167 // if ch > ichNew then ECAdjustIch adjusted ich. 04168 if (ch > ichNew) 04169 lpnew = ECAnsiNext(ped, lpnew); 04170 04171 return (ICH)(lpnew-lpstr); 04172 } 04173 04174 /***************************************************************************\ 04175 * ECUpdateFormat 04176 * 04177 * Computes ped->format and ped->fRtoLReading from dwStyle and dwExStyle. 04178 * Refreshes the display if either are changed. 04179 * 04180 * History: 04181 * May 12, 1997 [samera] wrote it 04182 * May 12, 1997 [dbrown] rewrote it 04183 \***************************************************************************/ 04184 04185 void ECUpdateFormat( 04186 PED ped, 04187 DWORD dwStyle, 04188 DWORD dwExStyle) 04189 { 04190 UINT fNewRtoLReading; 04191 UINT uiNewFormat; 04192 04193 // Extract new format and reading order from style 04194 04195 fNewRtoLReading = dwExStyle & WS_EX_RTLREADING ? 1 : 0; 04196 uiNewFormat = dwStyle & ES_FMTMASK; 04197 04198 04199 // WS_EX_RIGHT is ignored unless dwStyle is ES_LEFT 04200 04201 if (uiNewFormat == ES_LEFT && dwExStyle & WS_EX_RIGHT) { 04202 uiNewFormat = ES_RIGHT; 04203 } 04204 04205 04206 // Internally ES_LEFT and ES_RIGHT are swapped for RtoLReading order 04207 // (Think of them as ES_LEADING and ES_TRAILING) 04208 04209 if (fNewRtoLReading) { 04210 switch (uiNewFormat) { 04211 case ES_LEFT: uiNewFormat = ES_RIGHT; break; 04212 case ES_RIGHT: uiNewFormat = ES_LEFT; break; 04213 } 04214 } 04215 04216 04217 // Format change does not cause redisplay by itself 04218 04219 ped->format = uiNewFormat; 04220 04221 04222 // Refresh display on change of reading order 04223 04224 if (fNewRtoLReading != ped->fRtoLReading) { 04225 04226 ped->fRtoLReading = fNewRtoLReading; 04227 04228 if (ped->fWrap) { 04229 // Redo wordwrap 04230 MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL); 04231 MLUpdateiCaretLine(ped); 04232 } else { 04233 // Refresh horizontal scrollbar display 04234 MLScroll(ped, FALSE, 0xffffffff, 0, TRUE); 04235 } 04236 ECInvalidateClient(ped, TRUE); 04237 } 04238 }

Generated on Sat May 15 19:39:48 2004 for test by doxygen 1.3.7