Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4266 lines
138 KiB

/****************************************************************************\
* editec.c - Edit controls rewrite. Version II of edit controls.
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* Created: 24-Jul-88 davidds
\****************************************************************************/
/* Warning: The single line editcontrols contain internal styles and API which
* are need to support comboboxes. They are defined in combcom.h/combcom.inc
* and may be redefined or renumbered as needed.
*/
#include "precomp.h"
#pragma hdrstop
LOOKASIDE EditLookaside;
ICH ECFindTabA(LPSTR lpstr, ICH cch);
ICH ECFindTabW(LPWSTR lpstr, ICH cch);
#define umin(a, b) ((unsigned)(a) < (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
#define umax(a, b) ((unsigned)(a) > (unsigned)(b) ? (unsigned)(a) : (unsigned)(b))
#define UNICODE_CARRIAGERETURN ((WCHAR)0x0d)
#define UNICODE_LINEFEED ((WCHAR)0x0a)
#define UNICODE_TAB ((WCHAR)0x09)
// IME Menu IDs
#define ID_IMEOPENCLOSE 10001
#define ID_SOFTKBDOPENCLOSE 10002
#define ID_RECONVERTSTRING 10003
typedef struct {
DWORD fDisableCut : 1;
DWORD fDisablePaste : 1;
DWORD fNeedSeparatorBeforeImeMenu : 1;
DWORD fIME : 1;
} EditMenuItemState;
/***************************************************************************\
* Handlers common to both single and multi line edit controls.
/***************************************************************************/
/***************************************************************************\
* ECLock
*
* History:
\***************************************************************************/
PSTR ECLock(
PED ped)
{
PSTR ptext = LOCALLOCK(ped->hText, ped->hInstance);
ped->iLockLevel++;
/*
* If this is the first lock of the text and the text is encoded
* decode the text.
*/
//RIPMSG2(RIP_VERBOSE, "lock : %d '%10s'\n", ped->iLockLevel, ptext);
if (ped->iLockLevel == 1 && ped->fEncoded) {
/*
* rtlrundecode can't handle zero length strings
*/
if (ped->cch != 0) {
STRING string;
string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
string.Buffer = ptext;
RtlRunDecodeUnicodeString(ped->seed, (PUNICODE_STRING)&string);
//RIPMSG1(RIP_VERBOSE, "Decoding: '%10s'\n", ptext);
}
ped->fEncoded = FALSE;
}
return ptext;
}
/***************************************************************************\
* ECUnlock
*
* History:
\***************************************************************************/
void ECUnlock(
PED ped)
{
/*
* if we are removing the last lock on the text and the password
* character is set then encode the text
*/
//RIPMSG1(RIP_VERBOSE, "unlock: %d '%10s'\n", ped->iLockLevel, ped->ptext);
if (ped->charPasswordChar && ped->iLockLevel == 1 && ped->cch != 0) {
UNICODE_STRING string;
string.Length = string.MaximumLength = (USHORT)(ped->cch * ped->cbChar);
string.Buffer = LOCALLOCK(ped->hText, ped->hInstance);
RtlRunEncodeUnicodeString(&(ped->seed), &string);
//RIPMSG1(RIP_VERBOSE, "Encoding: '%10s'\n", ped->ptext);
ped->fEncoded = TRUE;
LOCALUNLOCK(ped->hText, ped->hInstance);
}
LOCALUNLOCK(ped->hText, ped->hInstance);
ped->iLockLevel--;
}
/***************************************************************************\
*
* GetActualNegA()
* For a given strip of text, this function computes the negative A width
* for the whole strip and returns the value as a postive number.
* It also fills the NegAInfo structure with details about the postion
* of this strip that results in this Negative A.
*
\***************************************************************************/
UINT GetActualNegA(
HDC hdc,
PED ped,
int x,
LPSTR lpstring,
ICH ichString,
int nCount,
LPSTRIPINFO NegAInfo)
{
int iCharCount, i;
int iLeftmostPoint = x;
PABC pABCwidthBuff;
UINT wCharIndex;
int xStartPoint = x;
ABC abc;
// To begin with, let us assume that there is no negative A width for
// this strip and initialize accodingly.
NegAInfo->XStartPos = x;
NegAInfo->lpString = lpstring;
NegAInfo->nCount = 0;
NegAInfo->ichString = ichString;
// If the current font is not a TrueType font, then there can not be any
// negative A widths.
if (!ped->fTrueType) {
if(!ped->charOverhang) {
return 0;
} else {
NegAInfo->nCount = min(nCount, (int)ped->wMaxNegAcharPos);
return ped->charOverhang;
}
}
// How many characters are to be considered for computing Negative A ?
iCharCount = min(nCount, (int)ped->wMaxNegAcharPos);
// Do we have the info on individual character's widths?
if(!ped->charWidthBuffer) {
// No! So, let us tell them to consider all the characters.
NegAInfo->nCount = iCharCount;
return(iCharCount * ped->aveCharWidth);
}
pABCwidthBuff = (PABC) ped->charWidthBuffer;
if (ped->fAnsi) {
for (i = 0; i < iCharCount; i++) {
wCharIndex = (UINT)(*((unsigned char *)lpstring));
if (*lpstring == VK_TAB) {
// To play it safe, we assume that this tab results in a tab length of
// 1 pixel because this is the minimum possible tab length.
x++;
} else {
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width.
else {
GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc) ;
x += abc.abcA;
}
if (x < iLeftmostPoint)
iLeftmostPoint = x; // Reset the leftmost point.
if (x < xStartPoint)
NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1.
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH ) {
x += pABCwidthBuff[wCharIndex].abcB + pABCwidthBuff[wCharIndex].abcC;
} else {
x += abc.abcB + abc.abcC;
}
}
lpstring++;
}
} else { // Unicode
LPWSTR lpwstring = (LPWSTR) lpstring ;
for (i = 0; i < iCharCount; i++) {
wCharIndex = *lpwstring ;
if (*lpwstring == VK_TAB) {
// To play it safe, we assume that this tab results in a tab length of
// 1 pixel because this is the minimum possible tab length.
x++;
} else {
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
x += pABCwidthBuff[wCharIndex].abcA; // Add the 'A' width.
else {
GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ;
x += abc.abcA ;
}
if (x < iLeftmostPoint)
iLeftmostPoint = x; // Reset the leftmost point.
if (x < xStartPoint)
NegAInfo->nCount = i+1; // 'i' is index; To get the count add 1.
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
x += pABCwidthBuff[wCharIndex].abcB +
pABCwidthBuff[wCharIndex].abcC;
else
x += abc.abcB + abc.abcC ;
}
lpwstring++;
}
}
// Let us return the negative A for the whole strip as a positive value.
return((UINT)(xStartPoint - iLeftmostPoint));
}
/***************************************************************************\
*
* ECIsAncestorActive()
*
* Returns whether or not we're the child of an "active" window. Looks for
* the first parent window that has a caption.
*
* This is a function because we might use it elsewhere when getting left
* clicked on, etc.
*
\***************************************************************************/
BOOL ECIsAncestorActive(HWND hwnd)
{
// We want to return TRUE always for top level windows. That's because
// of how WM_MOUSEACTIVATE works. If we see the click at all, the
// window is active. However, if we reach a child ancestor that has
// a caption, return the frame-on style bit.
//
// Note that calling FlashWindow() will have an effect. If the user
// clicks on an edit field in a child window that is flashed off, nothing
// will happen unless the window stops flashing and ncactivates first.
while (hwnd) {
PWND pwnd = ValidateHwnd( hwnd );
//
// Bail out if some parent window isn't 4.0 compatible or we've
// reached the top. Fixes compatibility problems with 3.x apps,
// especially MFC samples.
//
if (!TestWF(pwnd, WFWIN40COMPAT) || !TestWF(pwnd, WFCHILD))
hwnd = NULL; // to break us out of the loop
else if (TestWF(pwnd, WFCPRESENT))
return(TestWF(pwnd, WFFRAMEON) != 0);
else
hwnd = GetParent(hwnd);
}
return(TRUE);
}
/***************************************************************************\
* ECSetIMEMenu()
*
* support IME specific context menu
*
* Create: 30-Apr-97 Hiroyama : Ported from Memphis
\***************************************************************************/
BOOL ECSetIMEMenu(
HMENU hMenu,
HWND hwnd,
EditMenuItemState state)
{
MENUITEMINFO mii;
HIMC hIMC;
HKL hKL;
HMENU hmenuSub;
WCHAR szRes[32];
int nPrevLastItem;
int nItemsAdded = 0;
UserAssert(IS_IME_ENABLED() && state.fIME);
hKL = THREAD_HKL();
if (!fpImmIsIME(hKL))
return TRUE;
hIMC = fpImmGetContext(hwnd);
if (hIMC == NULL) {
// early out
return FALSE;
}
hmenuSub = GetSubMenu(hMenu, 0);
if (hmenuSub == NULL) {
return FALSE;
}
nPrevLastItem = GetMenuItemCount(hmenuSub);
if (hIMC) {
if (LOWORD(HandleToUlong(hKL)) != 0x412) {
//
// If Korean, do not show open/close menus
//
if (fpImmGetOpenStatus(hIMC))
LoadString(hmodUser, STR_IMECLOSE, szRes, ARRAYSIZE(szRes));
else
LoadString(hmodUser, STR_IMEOPEN, szRes, ARRAYSIZE(szRes));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING | MIIM_ID;
mii.dwTypeData = szRes;
mii.cch = 0xffff;
mii.wID = ID_IMEOPENCLOSE;
InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
++nItemsAdded;
}
if (fpImmGetProperty(hKL, IGP_CONVERSION) & IME_CMODE_SOFTKBD) {
DWORD fdwConversion;
fpImmGetConversionStatus(hIMC, &fdwConversion, NULL);
if (fdwConversion & IME_CMODE_SOFTKBD)
LoadString(hmodUser, STR_SOFTKBDCLOSE, szRes, ARRAYSIZE(szRes));
else
LoadString(hmodUser, STR_SOFTKBDOPEN, szRes, ARRAYSIZE(szRes));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING | MIIM_ID;
mii.dwTypeData = szRes;
mii.cch = 0xffff;
mii.wID = ID_SOFTKBDOPENCLOSE;
InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
++nItemsAdded;
}
if (LOWORD(HandleToUlong(hKL)) != 0x412) {
//
// If Korean, do not show reconversion menus
//
DWORD dwSCS = fpImmGetProperty(hKL, IGP_SETCOMPSTR);
LoadString(hmodUser, STR_RECONVERTSTRING, szRes, ARRAYSIZE(szRes));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE;
mii.dwTypeData = szRes;
mii.fState = 0;
mii.cch = 0xffff;
mii.wID = ID_RECONVERTSTRING;
if (state.fDisableCut ||
!(dwSCS & SCS_CAP_SETRECONVERTSTRING) ||
!(dwSCS & SCS_CAP_MAKEREAD)) {
mii.fState |= MFS_GRAYED;
}
InsertMenuItem(hmenuSub, 0xffff, TRUE, &mii);
++nItemsAdded;
}
}
//
// Add or remove the menu separator
//
if (state.fNeedSeparatorBeforeImeMenu && nItemsAdded != 0) {
//
// If the menu for Middle East has left a separator,
// fNeedSeparatorBeforeImeMenu is FALSE.
// I.e. we don't need to add more.
//
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
InsertMenuItem(hmenuSub, nPrevLastItem, TRUE, &mii);
}
else if (!state.fNeedSeparatorBeforeImeMenu && nItemsAdded == 0) {
//
// Extra separator is left by ME menus. Remove it.
//
UserVerify(NtUserDeleteMenu(hmenuSub, nPrevLastItem - 1, MF_BYPOSITION));
}
fpImmReleaseContext(hwnd, hIMC);
return TRUE;
}
void ECInOutReconversionMode(PED ped, BOOL fIn)
{
UserAssert(fIn == TRUE || fIn == FALSE);
if (fIn == ped->fInReconversion) {
return;
}
ped->fInReconversion = fIn;
if (ped->fFocus) {
(fIn ? NtUserHideCaret: NtUserShowCaret)(ped->hwnd);
}
return;
}
/***************************************************************************\
* ECDoIMEMenuCommand()
*
* support IME specific context menu
*
* Create: 30-Apr-97 Hiroyama : Ported from Memphis
\***************************************************************************/
BOOL NEAR ECDoIMEMenuCommand(PED ped, int cmd, HWND hwnd)
{
HIMC hIMC;
// early out
switch (cmd) {
case ID_IMEOPENCLOSE:
case ID_SOFTKBDOPENCLOSE:
case ID_RECONVERTSTRING:
break;
default:
return FALSE;
}
// everybody needs hIMC, so get it here
hIMC = fpImmGetContext(hwnd);
if (hIMC == NULL) {
// indicate to caller, that no further command processing needed
return TRUE;
}
switch (cmd) {
case ID_IMEOPENCLOSE:
{
// switch IME Open/Close status
BOOL fOpen = fpImmGetOpenStatus(hIMC);
fpImmSetOpenStatus(hIMC, !fOpen);
}
break;
case ID_SOFTKBDOPENCLOSE:
{
DWORD fdwConversion;
if (fpImmGetConversionStatus(hIMC, &fdwConversion, NULL)) {
//
// Toggle soft keyboard Open/Close status
//
fpImmEnumInputContext(0, SyncSoftKbdState,
(fdwConversion & IME_CMODE_SOFTKBD) != IME_CMODE_SOFTKBD);
}
}
break;
case ID_RECONVERTSTRING:
{
DWORD dwStrLen; // holds TCHAR count of recionversion string
DWORD cbLen; // holds BYTE SIZE of reconversion string
DWORD dwSize;
LPRECONVERTSTRING lpRCS;
// pass current selection to IME for reconversion
dwStrLen = ped->ichMaxSel - ped->ichMinSel;
cbLen = dwStrLen * ped->cbChar;
dwSize = cbLen + sizeof(RECONVERTSTRING) + 8;
lpRCS = (LPRECONVERTSTRING)UserLocalAlloc(0, dwSize);
if (lpRCS) {
LPBYTE pText;
ICH ichSelMinOrg;
ichSelMinOrg = ped->ichMinSel;
pText = ECLock(ped);
if (pText != NULL) {
LPBYTE lpDest;
BOOL (WINAPI* fpSetCompositionStringAW)(HIMC, DWORD, LPCVOID, DWORD, LPCVOID, DWORD);
lpRCS->dwSize = dwSize;
lpRCS->dwVersion = 0;
lpRCS->dwStrLen =
lpRCS->dwCompStrLen =
lpRCS->dwTargetStrLen = dwStrLen;
lpRCS->dwStrOffset = sizeof(RECONVERTSTRING);
lpRCS->dwCompStrOffset =
lpRCS->dwTargetStrOffset = 0;
lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING);
RtlCopyMemory(lpDest, pText + ped->ichMinSel * ped->cbChar, cbLen);
if (ped->fAnsi) {
LPBYTE psz = (LPBYTE)lpDest;
psz[cbLen] = '\0';
fpSetCompositionStringAW = fpImmSetCompositionStringA;
} else {
LPWSTR pwsz = (LPWSTR)lpDest;
pwsz[dwStrLen] = L'\0';
fpSetCompositionStringAW = fpImmSetCompositionStringW;
}
ECUnlock(ped);
UserAssert(fpSetCompositionStringAW != NULL);
ECInOutReconversionMode(ped, TRUE);
ECImmSetCompositionWindow(ped, 0, 0); // x and y will be overriden anyway
// Query the IME for a valid Reconvert string range first.
fpSetCompositionStringAW(hIMC, SCS_QUERYRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
// If current IME updates the original reconvert structure,
// it is necessary to update the text selection based on the
// new reconvert text range.
if ((lpRCS->dwCompStrLen != dwStrLen) || (ichSelMinOrg != ped->ichMinSel)) {
ICH ichSelStart;
ICH ichSelEnd;
ichSelStart = ichSelMinOrg + (lpRCS->dwCompStrOffset / ped->cbChar);
ichSelEnd = ichSelStart + lpRCS->dwCompStrLen;
(ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd);
}
fpSetCompositionStringAW(hIMC, SCS_SETRECONVERTSTRING, lpRCS, dwSize, NULL, 0);
} // pText
UserLocalFree(lpRCS);
}
}
break;
default:
// should never reach here.
RIPMSG1(RIP_ERROR, "ECDoIMEMenuCommand: unknown command id %d; should never reach here.", cmd);
return FALSE;
}
UserAssert(hIMC != NULL);
fpImmReleaseContext(hwnd, hIMC);
return TRUE;
}
/***************************************************************************\
*
* ECMenu()
*
* Handles context menu for edit fields. Disables inappropriate commands.
* Note that this is NOT subclassing friendly, like most of our functions,
* for speed and convenience.
*
\***************************************************************************/
void ECMenu(
HWND hwnd,
PED ped,
LPPOINT pt)
{
HMENU hMenu;
int cmd = 0;
int x;
int y;
UINT uFlags = TPM_NONOTIFY | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON;
EditMenuItemState state = {
FALSE, // fDisableCut
TRUE, // fDisablePaste
TRUE, // fNeedSeparatorBeforeImeMenu
IS_IME_ENABLED() && fpImmIsIME(THREAD_HKL()), // fIME
};
// Set focus if we don't have it.
if (!ped->fFocus)
NtUserSetFocus(hwnd);
// Grab the menu from USER's resources...
if (!(hMenu = LoadMenu( hmodUser, MAKEINTRESOURCE( ID_EC_PROPERTY_MENU ))))
return ;
// Undo -- not allowed if we have no saved undo info
if (ped->undoType == UNDO_NONE)
EnableMenuItem(hMenu, WM_UNDO, MF_BYCOMMAND | MFS_GRAYED);
if (ped->fReadOnly || ped->charPasswordChar) {
// Cut and Delete -- not allowed if read-only or password
state.fDisableCut = TRUE;
} else {
// Cut, Delete -- not allowed if there's no selection
if (ped->ichMinSel == ped->ichMaxSel)
state.fDisableCut = TRUE;
}
// Paste -- not allowed if there's no text on the clipboard
// (this works for both OEM and Unicode)
// Used to be always disabled for password edits MCostea #221035
if (NtUserIsClipboardFormatAvailable(CF_TEXT))
state.fDisablePaste = FALSE;
if (state.fDisableCut) {
EnableMenuItem(hMenu, WM_CUT, MF_BYCOMMAND | MFS_GRAYED);
EnableMenuItem(hMenu, WM_CLEAR, MF_BYCOMMAND | MFS_GRAYED);
}
if (state.fDisablePaste)
EnableMenuItem(hMenu, WM_PASTE, MF_BYCOMMAND | MFS_GRAYED);
// Copy -- not allowed if there's no selection or password ec
if ((ped->ichMinSel == ped->ichMaxSel) || (ped->charPasswordChar))
EnableMenuItem(hMenu, WM_COPY, MF_BYCOMMAND | MFS_GRAYED);
// Select All -- not allowed if there's no text or if everything is
// selected. Latter case takes care of first one.
if ((ped->ichMinSel == 0) && (ped->ichMaxSel == ped->cch))
EnableMenuItem(hMenu, EM_SETSEL, MF_BYCOMMAND | MFS_GRAYED);
if (ped->pLpkEditCallout) {
ped->pLpkEditCallout->EditSetMenu(ped, hMenu);
} else {
NtUserDeleteMenu(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND);
NtUserDeleteMenu(hMenu, ID_CNTX_RTL, MF_BYCOMMAND);
NtUserDeleteMenu(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND);
if (state.fIME) {
// One separator is left in the menu,
// no need to add the one before IME menus
state.fNeedSeparatorBeforeImeMenu = FALSE;
} else {
// Extra separator is left. Remove it.
HMENU hmenuSub = GetSubMenu(hMenu, 0);
int nItems = GetMenuItemCount(hmenuSub) - 1;
UserAssert(nItems >= 0);
UserAssert(GetMenuState(hmenuSub, nItems, MF_BYPOSITION) & MF_SEPARATOR);
// remove needless separator
UserVerify(NtUserDeleteMenu(hmenuSub, nItems, MF_BYPOSITION));
}
}
// IME specific menu
if (state.fIME) {
ECSetIMEMenu(hMenu, hwnd, state);
}
// BOGUS
// We position the menu below & to the right of the point clicked on.
// Is this cool? I think so. Excel 4.0 does the same thing. It
// seems like it would be neat if we could avoid obscuring the
// selection. But in actuality, it seems even more awkward to move
// the menu out of the way of the selection. The user can't click
// and drag that way, and they have to move the mouse a ton.
//
// We need to use TPM_NONOTIFY because VBRUN100 and VBRUN200 GP-fault
// on unexpected menu messages.
//
/*
* if message came via the keyboard then center on the control
* We use -1 && -1 here not 0xFFFFFFFF like Win95 becuase we
* previously converted the lParam to a point with sign extending.
*/
if (pt->x == -1 && pt->y == -1) {
RECT rc;
GetWindowRect(hwnd, &rc);
x = rc.left + (rc.right - rc.left) / 2;
y = rc.top + (rc.bottom - rc.top) / 2;
} else {
x = pt->x;
y = pt->y;
}
if (RTL_UI()) {
uFlags |= TPM_LAYOUTRTL;
}
cmd = NtUserTrackPopupMenuEx(GetSubMenu(hMenu, 0), uFlags, x, y, hwnd, NULL);
// Free our menu
NtUserDestroyMenu(hMenu);
if (cmd && (cmd != -1)) {
if (ped->pLpkEditCallout && cmd) {
ped->pLpkEditCallout->EditProcessMenu(ped, cmd);
}
if (!state.fIME || !ECDoIMEMenuCommand(ped, cmd, hwnd)) {
// if cmd is not IME specific menu, send it.
SendMessage(hwnd, cmd, 0, (cmd == EM_SETSEL) ? 0xFFFFFFFF : 0L );
}
}
}
/***************************************************************************\
*
* ECClearText()
*
* Clears selected text. Does NOT _send_ a fake char backspace.
*
\***************************************************************************/
void ECClearText(PED ped) {
if (!ped->fReadOnly &&
(ped->ichMinSel < ped->ichMaxSel)) {
if (ped->fSingle)
SLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L );
else
MLEditWndProc(ped->hwnd, ped, WM_CHAR, VK_BACK, 0L );
}
}
/***************************************************************************\
*
* ECCutText() -
*
* Cuts selected text. This removes and copies the selection to the clip,
* or if nothing is selected we delete (clear) the left character.
*
\***************************************************************************/
void ECCutText(PED ped) {
// Cut selection--IE, remove and copy to clipboard, or if no selection,
// delete (clear) character left.
if (!ped->fReadOnly &&
(ped->ichMinSel < ped->ichMaxSel) &&
SendMessage(ped->hwnd, WM_COPY, 0, 0L)) {
// If copy was successful, delete the copied text by sending a
// backspace message which will redraw the text and take care of
// notifying the parent of changes.
ECClearText(ped);
}
}
/***************************************************************************\
*
* ECGetModKeys()
*
* Gets modifier key states. Currently, we only check for VK_CONTROL and
* VK_SHIFT.
*
\***************************************************************************/
int ECGetModKeys(int keyMods) {
int scState;
scState = 0;
if (!keyMods) {
if (GetKeyState(VK_CONTROL) < 0)
scState |= CTRLDOWN;
if (GetKeyState(VK_SHIFT) < 0)
scState |= SHFTDOWN;
} else if (keyMods != NOMODIFY)
scState = keyMods;
return scState;
}
/***************************************************************************\
*
* ECTabTheTextOut() AorW
* If fDrawText == FALSE, then this function returns the text extent of
* of the given strip of text. It does not worry about the Negative widths.
*
* If fDrawText == TRUE, this draws the given strip of Text expanding the
* tabs to proper lengths, calculates and fills up the NegCInfoForStrip with
* details required to draw the portions of this strip that goes beyond the
* xClipEndPos due to Negative C widths.
*
* Returns the max width AS A DWORD. We don't care about the height
* at all. No one uses it. We keep a DWORD because that way we avoid
* overflow.
*
* NOTE: If the language pack is loaded EcTabTheTextOut is not used - the
* language pack must take care of all tab expansion and selection
* highlighting with full support for bidi layout and complex script
* glyph reordering.
*
\***************************************************************************/
UINT ECTabTheTextOut(
HDC hdc,
int xClipStPos,
int xClipEndPos,
int xStart,
int y,
LPSTR lpstring,
int nCount,
ICH ichString,
PED ped,
int iTabOrigin,
BOOL fDraw,
LPSTRIPINFO NegCInfoForStrip)
{
int nTabPositions; // Count of tabstops in tabstop array.
LPINT lpintTabStopPositions; // Tab stop positions in pixels.
int cch;
UINT textextent;
int xEnd;
int pixeltabstop = 0;
int i;
int cxCharWidth;
RECT rc;
BOOL fOpaque;
BOOL fFirstPass = TRUE;
PINT charWidthBuff;
int iTabLength;
int nConsecutiveTabs;
int xStripStPos;
int xStripEndPos;
int xEndOfStrip;
STRIPINFO RedrawStripInfo;
STRIPINFO NegAInfo;
LPSTR lpTab;
LPWSTR lpwTab;
UINT wNegCwidth, wNegAwidth;
int xRightmostPoint = xClipStPos;
int xTabStartPos;
int iSavedBkMode = 0;
WCHAR wchar;
SIZE size;
ABC abc ;
// Algorithm: Draw the strip opaquely first. If a tab length is so
// small that the portions of text on either side of a tab overlap with
// the other, then this will result in some clipping. So, such portion
// of the strip is remembered in "RedrawStripInfo" and redrawn
// transparently later to compensate the clippings.
// NOTE: "RedrawStripInfo" can hold info about just one portion. So, if
// more than one portion of the strip needs to be redrawn transparently,
// then we "merge" all such portions into a single strip and redraw that
// strip at the end.
if (fDraw) {
// To begin with, let us assume that there is no Negative C for this
// strip and initialize the Negative Width Info structure.
NegCInfoForStrip->nCount = 0;
NegCInfoForStrip->XStartPos = xClipEndPos;
// We may not have to redraw any portion of this strip.
RedrawStripInfo.nCount = 0;
fOpaque = (GetBkMode(hdc) == OPAQUE) || (fDraw == ECT_SELECTED);
}
#if DBG
else {
//
// Both MLGetLineWidth() and ECCchInWidth() should be clipping
// nCount to avoid overflow.
//
if (nCount > MAXLINELENGTH)
RIPMSG0(RIP_WARNING, "ECTabTheTextOut: nCount > MAXLINELENGTH");
}
#endif
// Let us define the Clip rectangle.
rc.left = xClipStPos;
rc.right = xClipEndPos;
rc.top = y;
rc.bottom = y + ped->lineHeight;
// Check if anything needs to be drawn.
if (!lpstring || !nCount) {
if (fDraw)
ExtTextOutW(hdc, xClipStPos, y,
(fOpaque ? ETO_OPAQUE | ETO_CLIPPED : ETO_CLIPPED),
&rc, L"", 0, 0L);
return(0L);
}
//
// Starting position
//
xEnd = xStart;
cxCharWidth = ped->aveCharWidth;
nTabPositions = (ped->pTabStops ? *(ped->pTabStops) : 0);
if (ped->pTabStops) {
lpintTabStopPositions = (LPINT)(ped->pTabStops+1);
if (nTabPositions == 1) {
pixeltabstop = lpintTabStopPositions[0];
if (!pixeltabstop)
pixeltabstop = 1;
}
} else {
lpintTabStopPositions = NULL;
pixeltabstop = 8*cxCharWidth;
}
// The first time we will draw the strip Opaquely. If some portions need
// to be redrawn , then we will set the mode to TRANSPARENT and
// jump to this location to redraw those portions.
RedrawStrip:
while (nCount) {
wNegCwidth = ped->wMaxNegC;
// Search for the first TAB in this strip; also compute the extent
// of the the strip upto and not including the tab character.
//
// Note - If the langpack is loaded, there will be no charWidthBuffer.
//
if (ped->charWidthBuffer) { // Do we have a character width buffer?
textextent = 0;
cch = nCount;
if (ped->fTrueType) { // If so, does it have ABC widths?
UINT iRightmostPoint = 0;
UINT wCharIndex;
PABC pABCwidthBuff;
pABCwidthBuff = (PABC) ped->charWidthBuffer;
if ( ped->fAnsi ) {
for (i = 0; i < nCount; i++) {
if (lpstring[i] == VK_TAB) {
cch = i;
break;
}
wCharIndex = (UINT)(((unsigned char *)lpstring)[i]);
if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) {
textextent += (UINT)(pABCwidthBuff[wCharIndex].abcA +
pABCwidthBuff[wCharIndex].abcB);
} else { // not in cache, will ask driver
GetCharABCWidthsA(hdc, wCharIndex, wCharIndex, &abc);
textextent += abc.abcA + abc.abcB ;
}
if (textextent > iRightmostPoint)
iRightmostPoint = textextent;
if (wCharIndex < CHAR_WIDTH_BUFFER_LENGTH) {
textextent += pABCwidthBuff[wCharIndex].abcC;
} else { // not in cache
textextent += abc.abcC;
}
if (textextent > iRightmostPoint)
iRightmostPoint = textextent;
}
} else { // Unicode
for (i = 0; i < nCount; i++) {
WCHAR UNALIGNED * lpwstring = (WCHAR UNALIGNED *)lpstring;
if (lpwstring[i] == VK_TAB) {
cch = i;
break;
}
wCharIndex = lpwstring[i] ;
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
textextent += pABCwidthBuff[wCharIndex].abcA +
pABCwidthBuff[wCharIndex].abcB;
else {
GetCharABCWidthsW(hdc, wCharIndex, wCharIndex, &abc) ;
textextent += abc.abcA + abc.abcB ;
}
/*
* Note that abcC could be negative so we need this
* statement here *and* below
*/
if (textextent > iRightmostPoint)
iRightmostPoint = textextent;
if ( wCharIndex < CHAR_WIDTH_BUFFER_LENGTH )
textextent += pABCwidthBuff[wCharIndex].abcC;
else
textextent += abc.abcC ;
if (textextent > iRightmostPoint)
iRightmostPoint = textextent;
}
}
wNegCwidth = (int)(iRightmostPoint - textextent);
} else { // !ped->fTrueType
// No! This is not a TrueType font; So, we have only character
// width info in this buffer.
charWidthBuff = ped->charWidthBuffer;
if ( ped->fAnsi ) {
// Initially assume no tabs exist in the text so cch=nCount.
for (i = 0; i < nCount; i++) {
if (lpstring[i] == VK_TAB) {
cch = i;
break;
}
//
// Call GetTextExtentPoint for dbcs/hankaku characters
//
if (ped->fDBCS && (i+1 < nCount)
&& ECIsDBCSLeadByte(ped,lpstring[i])) {
GetTextExtentPointA(hdc, &lpstring[i], 2, &size);
textextent += size.cx;
i++;
} else if ((UCHAR)lpstring[i] >= CHAR_WIDTH_BUFFER_LENGTH) {
// Skip this GetExtentPoint call for non hankaku code points
// Or if the character is in the width cache.
GetTextExtentPointA(hdc, &lpstring[i], 1, &size);
textextent += size.cx;
} else {
textextent += (UINT)(charWidthBuff[(UINT)(((unsigned char *)lpstring)[i])]);
}
}
} else {
LPWSTR lpwstring = (LPWSTR) lpstring ;
INT cchUStart; // start of unicode character count
for (i = 0; i < nCount; i++) {
if (lpwstring[i] == VK_TAB) {
cch = i;
break;
}
wchar = lpwstring[i];
if (wchar >= CHAR_WIDTH_BUFFER_LENGTH) {
/*
* We have a Unicode character that is not in our
* cache, get all the characters outside the cache
* before getting the text extent on this part of the
* string.
*/
cchUStart = i;
while (wchar >= CHAR_WIDTH_BUFFER_LENGTH &&
wchar != VK_TAB && i < nCount) {
wchar = lpwstring[++i];
}
GetTextExtentPointW(hdc, (LPWSTR)lpwstring + cchUStart,
i-cchUStart, &size);
textextent += size.cx;
if (wchar == VK_TAB || i >= nCount) {
cch = i;
break;
}
/*
* We have a char that is in the cache, fall through.
*/
}
/*
* The width of this character is in the cache buffer.
*/
textextent += ped->charWidthBuffer[wchar];
}
}
} // fTrueType else.
nCount -= cch;
} else { // If we don't have a buffer that contains the width info.
/*
* Gotta call the driver to do our text extent.
*/
if ( ped->fAnsi ) {
cch = (int)ECFindTabA(lpstring, nCount);
GetTextExtentPointA(hdc, lpstring, cch, &size) ;
} else {
cch = (int)ECFindTabW((LPWSTR) lpstring, nCount);
GetTextExtentPointW(hdc, (LPWSTR)lpstring, cch, &size);
}
nCount -= cch;
//
// Subtruct Overhang for Italic fonts.
//
textextent = (size.cx - ped->charOverhang);
}
//
// textextent is computed.
//
xStripStPos = xEnd;
xEnd += (int)textextent;
xStripEndPos = xEnd;
// We will consider the negative widths only if when we draw opaquely.
if (fFirstPass && fDraw) {
xRightmostPoint = max(xStripEndPos + (int)wNegCwidth, xRightmostPoint);
// Check if this strip peeps beyond the clip region.
if (xRightmostPoint > xClipEndPos) {
if (!NegCInfoForStrip->nCount) {
NegCInfoForStrip->lpString = lpstring;
NegCInfoForStrip->ichString = ichString;
NegCInfoForStrip->nCount = nCount+cch;
NegCInfoForStrip->XStartPos = xStripStPos;
}
}
} /* if (fFirstPass && fDraw) */
if ( ped->fAnsi )
lpTab = lpstring + cch; // Possibly Points to a tab character.
else
lpwTab = ((LPWSTR)lpstring) + cch ;
// we must consider all the consecutive tabs and calculate the
// the begining of next strip.
nConsecutiveTabs = 0;
while (nCount &&
(ped->fAnsi ? (*lpTab == VK_TAB) : (*lpwTab == VK_TAB))) {
// Find the next tab position and update the x value.
xTabStartPos = xEnd;
if (pixeltabstop)
xEnd = (((xEnd-iTabOrigin)/pixeltabstop)*pixeltabstop) +
pixeltabstop + iTabOrigin;
else {
for (i = 0; i < nTabPositions; i++) {
if (xEnd < (lpintTabStopPositions[i] + iTabOrigin)) {
xEnd = (lpintTabStopPositions[i] + iTabOrigin);
break;
}
}
// Check if all the tabstops set are exhausted; Then start using
// default tab stop positions.
if (i == nTabPositions) {
pixeltabstop = 8*cxCharWidth;
xEnd = ((xEnd - iTabOrigin)/pixeltabstop)*pixeltabstop +
pixeltabstop + iTabOrigin;
}
}
if (fFirstPass && fDraw) {
xRightmostPoint = max(xEnd, xRightmostPoint);
/* Check if this strip peeps beyond the clip region */
if (xRightmostPoint > xClipEndPos) {
if (!NegCInfoForStrip->nCount) {
NegCInfoForStrip->ichString = ichString + cch + nConsecutiveTabs;
NegCInfoForStrip->nCount = nCount;
NegCInfoForStrip->lpString = (ped->fAnsi ?
lpTab : (LPSTR) lpwTab);
NegCInfoForStrip->XStartPos = xTabStartPos;
}
}
} /* if(fFirstPass) */
nConsecutiveTabs++;
nCount--;
ped->fAnsi ? lpTab++ : (LPSTR) (lpwTab++) ; // Move to the next character.
} // while(*lpTab == TAB) //
if (fDraw) {
if (fFirstPass) {
// Is anything remaining to be drawn in this strip?
if (!nCount)
rc.right = xEnd; // No! We are done.
else {
// "x" is the effective starting position of next strip.
iTabLength = xEnd - xStripEndPos;
// Check if there is a possibility of this tab length being too small
// compared to the negative A and C widths if any.
if ((wNegCwidth + (wNegAwidth = ped->wMaxNegA)) > (UINT)iTabLength) {
// Unfortunately, there is a possiblity of an overlap.
// Let us find out the actual NegA for the next strip.
wNegAwidth = GetActualNegA(
hdc,
ped,
xEnd,
lpstring + (cch + nConsecutiveTabs)*ped->cbChar,
ichString + cch + nConsecutiveTabs,
nCount,
&NegAInfo);
}
// Check if they actually overlap //
if ((wNegCwidth + wNegAwidth) <= (UINT)iTabLength) {
// No overlap between the strips. This is the ideal situation.
rc.right = xEnd - wNegAwidth;
} else {
// Yes! They overlap.
rc.right = xEnd;
// See if negative C width is too large compared to tab length.
if (wNegCwidth > (UINT)iTabLength) {
// Must redraw transparently a part of the current strip later.
if (RedrawStripInfo.nCount) {
// A previous strip also needs to be redrawn; So, merge this
// strip to that strip.
RedrawStripInfo.nCount = (ichString -
RedrawStripInfo.ichString) + cch;
} else {
RedrawStripInfo.nCount = cch;
RedrawStripInfo.lpString = lpstring;
RedrawStripInfo.ichString = ichString;
RedrawStripInfo.XStartPos = xStripStPos;
}
}
if (wNegAwidth) {
// Must redraw transparently the first part of the next strip later.
if (RedrawStripInfo.nCount) {
// A previous strip also needs to be redrawn; So, merge this
// strip to that strip.
RedrawStripInfo.nCount = (NegAInfo.ichString - RedrawStripInfo.ichString) +
NegAInfo.nCount;
} else
RedrawStripInfo = NegAInfo;
}
}
} // else (!nCount) //
} // if (fFirstPass) //
if (rc.left < xClipEndPos) {
if (fFirstPass) {
// If this is the end of the strip, then complete the rectangle.
if ((!nCount) && (xClipEndPos == MAXCLIPENDPOS))
rc.right = max(rc.right, xClipEndPos);
else
rc.right = min(rc.right, xClipEndPos);
}
// Draw the current strip.
if (rc.left < rc.right)
if ( ped->fAnsi )
ExtTextOutA(hdc,
xStripStPos,
y,
(fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
(LPRECT)&rc, lpstring, cch, 0L);
else
ExtTextOutW(hdc,
xStripStPos,
y,
(fFirstPass && fOpaque ? (ETO_OPAQUE | ETO_CLIPPED) : ETO_CLIPPED),
(LPRECT)&rc, (LPWSTR)lpstring, cch, 0L);
}
if (fFirstPass)
rc.left = max(rc.right, xClipStPos);
ichString += (cch+nConsecutiveTabs);
} // if (fDraw) //
// Skip over the tab and the characters we just drew.
lpstring += (cch + nConsecutiveTabs) * ped->cbChar;
} // while (nCount) //
xEndOfStrip = xEnd;
// check if we need to draw some portions transparently.
if (fFirstPass && fDraw && RedrawStripInfo.nCount) {
iSavedBkMode = SetBkMode(hdc, TRANSPARENT);
fFirstPass = FALSE;
nCount = RedrawStripInfo.nCount;
rc.left = xClipStPos;
rc.right = xClipEndPos;
lpstring = RedrawStripInfo.lpString;
ichString = RedrawStripInfo.ichString;
xEnd = RedrawStripInfo.XStartPos;
goto RedrawStrip; // Redraw Transparently.
}
if (iSavedBkMode) // Did we change the Bk mode?
SetBkMode(hdc, iSavedBkMode); // Then, let us set it back!
return((UINT)(xEndOfStrip - xStart));
}
/***************************************************************************\
* ECCchInWidth AorW
*
* Returns maximum count of characters (up to cch) from the given
* string (starting either at the beginning and moving forward or at the
* end and moving backwards based on the setting of the fForward flag)
* which will fit in the given width. ie. Will tell you how much of
* lpstring will fit in the given width even when using proportional
* characters. WARNING: If we use kerning, then this loses...
*
* History:
*
* NOTE: ECCchInWidth is not called if the language pack is loaded.
\***************************************************************************/
ICH ECCchInWidth(
PED ped,
HDC hdc,
LPSTR lpText,
ICH cch,
int width,
BOOL fForward)
{
int stringExtent;
int cchhigh;
int cchnew = 0;
int cchlow = 0;
SIZE size;
LPSTR lpStart;
if ((width <= 0) || !cch)
return (0);
/*
* Optimize nonproportional fonts for single line ec since they don't have
* tabs.
*/
//
// Change optimize condition for fixed pitch font
//
if (ped->fNonPropFont && ped->fSingle && !ped->fDBCS) {
return (ECAdjustIch( ped, lpText, umin(width/ped->aveCharWidth,(int)cch)));
}
/*
* Check if password hidden chars are being used.
*/
if (ped->charPasswordChar) {
return (umin(width / ped->cPasswordCharWidth, (int)cch));
}
/*
* ALWAYS RESTRICT TO AT MOST MAXLINELENGTH to avoid overflow...
*/
cch = umin(MAXLINELENGTH, cch);
cchhigh = cch + 1;
while (cchlow < cchhigh - 1) {
cchnew = umax((cchhigh - cchlow) / 2, 1) + cchlow;
lpStart = lpText;
/*
* If we want to figure out how many fit starting at the end and moving
* backwards, make sure we move to the appropriate position in the
* string before calculating the text extent.
*/
if (!fForward)
lpStart += (cch - cchnew)*ped->cbChar;
if (ped->fSingle) {
if (ped->fAnsi)
GetTextExtentPointA(hdc, (LPSTR)lpStart, cchnew, &size);
else
GetTextExtentPointW(hdc, (LPWSTR)lpStart, cchnew, &size);
stringExtent = size.cx;
} else {
stringExtent = ECTabTheTextOut(hdc, 0, 0, 0, 0,
lpStart,
cchnew, 0,
ped, 0, ECT_CALC, NULL );
}
if (stringExtent > width) {
cchhigh = cchnew;
} else {
cchlow = cchnew;
}
}
//
// Call ECAdjustIch ( generic case )
//
cchlow = ECAdjustIch( ped, lpText, cchlow );
return (cchlow);
}
/***************************************************************************\
* ECFindTab
*
* Scans lpstr and return s the number of CHARs till the first TAB.
* Scans at most cch chars of lpstr.
*
* History:
\***************************************************************************/
ICH ECFindTabA(
LPSTR lpstr,
ICH cch)
{
LPSTR copylpstr = lpstr;
if (!cch)
return 0;
while (*lpstr != VK_TAB) {
lpstr++;
if (--cch == 0)
break;
}
return ((ICH)(lpstr - copylpstr));
}
ICH ECFindTabW(
LPWSTR lpstr,
ICH cch)
{
LPWSTR copylpstr = lpstr;
if (!cch)
return 0;
while (*lpstr != VK_TAB) {
lpstr++;
if (--cch == 0)
break;
}
return ((ICH)(lpstr - copylpstr));
}
/***************************************************************************\
*
* ECGetBrush()
*
* Gets appropriate background brush to erase with.
*
\***************************************************************************/
HBRUSH ECGetBrush(PED ped, HDC hdc)
{
HBRUSH hbr;
BOOL f40Compat;
f40Compat = (GETAPPVER() >= VER40);
// Get background brush
if ((ped->fReadOnly || ped->fDisabled) && f40Compat) {
hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLORSTATIC);
} else
hbr = ECGetControlBrush(ped, hdc, WM_CTLCOLOREDIT);
if (ped->fDisabled && (ped->fSingle || f40Compat)) {
DWORD rgb;
// Change text color
rgb = GetSysColor(COLOR_GRAYTEXT);
if (rgb != GetBkColor(hdc))
SetTextColor(hdc, rgb);
}
return(hbr);
}
/***************************************************************************\
* NextWordCallBack
*
*
*
* History:
* 02-19-92 JimA Ported from Win31 sources.
\***************************************************************************/
void NextWordCallBack(
PED ped,
ICH ichStart,
BOOL fLeft,
ICH *pichMin,
ICH *pichMax )
{
ICH ichMinSel;
ICH ichMaxSel;
LPSTR pText;
pText = ECLock(ped);
if (fLeft || (!(BOOL)CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText,
ichStart, ped->cch, WB_ISDELIMITER) &&
(ped->fAnsi ? (*(pText + ichStart) != VK_RETURN) : (*((LPWSTR)pText + ichStart) != VK_RETURN))
))
ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_LEFT);
else
ichMinSel = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)pText, ichStart, ped->cch, WB_RIGHT);
ichMaxSel = min(ichMinSel + 1, ped->cch);
if (ped->fAnsi) {
if (*(pText + ichMinSel) == VK_RETURN) {
if (ichMinSel > 0 && *(pText + ichMinSel - 1) == VK_RETURN) {
/*
* So that we can treat CRCRLF as one word also.
*/
ichMinSel--;
} else if (*(pText+ichMinSel + 1) == VK_RETURN) {
/*
* Move MaxSel on to the LF
*/
ichMaxSel++;
}
}
} else {
if (*((LPWSTR)pText + ichMinSel) == VK_RETURN) {
if (ichMinSel > 0 && *((LPWSTR)pText + ichMinSel - 1) == VK_RETURN) {
/*
* So that we can treat CRCRLF as one word also.
*/
ichMinSel--;
} else if (*((LPWSTR)pText+ichMinSel + 1) == VK_RETURN) {
/*
* Move MaxSel on to the LF
*/
ichMaxSel++;
}
}
}
ichMaxSel = CALLWORDBREAKPROC(ped->lpfnNextWord, (LPSTR)pText, ichMaxSel, ped->cch, WB_RIGHT);
ECUnlock(ped);
if (pichMin) *pichMin = ichMinSel;
if (pichMax) *pichMax = ichMaxSel;
}
/***************************************************************************\
* NextWordLpkCallback
*
* Identifies next/prev word position for complex scripts
*
* History:
* 04-22-97 DBrown
\***************************************************************************/
void NextWordLpkCallBack(
PED ped,
ICH ichStart,
BOOL fLeft,
ICH *pichMin,
ICH *pichMax)
{
PSTR pText = ECLock(ped);
HDC hdc = ECGetEditDC(ped, TRUE);
ped->pLpkEditCallout->EditNextWord(ped, hdc, pText, ichStart, fLeft, pichMin, pichMax);
ECReleaseEditDC(ped, hdc, TRUE);
ECUnlock(ped);
}
/***************************************************************************\
* ECWordAorW
*
* if fLeft, Returns the ichMinSel and ichMaxSel of the word to the
* left of ichStart. ichMinSel contains the starting letter of the word,
* ichmaxsel contains all spaces up to the first character of the next word.
*
* if !fLeft, Returns the ichMinSel and ichMaxSel of the word to the right of
* ichStart. ichMinSel contains the starting letter of the word, ichmaxsel
* contains the first letter of the next word. If ichStart is in the middle
* of a word, that word is considered the left or right word.
*
* A CR LF pair or CRCRLF triple is considered a single word in
* multiline edit controls.
*
* History:
\***************************************************************************/
void ECWord(
PED ped,
ICH ichStart,
BOOL fLeft,
ICH *pichMin,
ICH *pichMax )
{
BOOL charLocated = FALSE;
BOOL spaceLocated = FALSE;
if ((!ichStart && fLeft) || (ichStart == ped->cch && !fLeft)) {
/*
* We are at the beginning of the text (looking left) or we are at end
* of text (looking right), no word here
*/
if (pichMin) *pichMin=0;
if (pichMax) *pichMax=0;
return;
}
/*
* Don't give out hints about word breaks if password chars are being used,
*/
if (ped->charPasswordChar) {
if (pichMin) *pichMin=0;
if (pichMax) *pichMax=ped->cch;
return;
}
if (ped->fAnsi) {
PSTR pText;
PSTR pWordMinSel;
PSTR pWordMaxSel;
PSTR pPrevChar;
UserAssert(ped->cbChar == sizeof(CHAR));
if (ped->lpfnNextWord) {
NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
return;
}
if (ped->pLpkEditCallout) {
NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
return;
}
pText = ECLock(ped);
pWordMinSel = pWordMaxSel = pText + ichStart;
/*
* if fLeft: Move pWordMinSel to the left looking for the start of a word.
* If we start at a space, we will include spaces in the selection as we
* move left untill we find a nonspace character. At that point, we continue
* looking left until we find a space. Thus, the selection will consist of
* a word with its trailing spaces or, it will consist of any leading at the
* beginning of a line of text.
*/
/*
* if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
* word. If the pWordMinSel points to a character, then we move left
* looking for a space which will signify the start of the word. If
* pWordMinSel points to a space, we look right till we come upon a
* character. pMaxWord will look right starting at pMinWord looking for the
* end of the word and its trailing spaces.
*/
if (fLeft || !ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0D) {
/*
* If we are moving left or if we are moving right and we are not on a
* space or a CR (the start of a word), then we was look left for the
* start of a word which is either a CR or a character. We do this by
* looking left till we find a character (or if CR we stop), then we
* continue looking left till we find a space or LF.
*/
while (pWordMinSel > pText && ((!ISDELIMETERA(*(pWordMinSel - 1)) &&
*(pWordMinSel - 1) != 0x0A) || !charLocated)) {
/*
* Treat double byte character as a word ( in ansi pWordMinSel loop )
*/
pPrevChar = ECAnsiPrev( ped, pText, pWordMinSel );
/*
** we are looking right ( !fLeft ).
** if current character is a double byte chararacter or
** previous character is a double byte character, we
** are on the beggining of a word.
*/
if ( !fLeft && ( ISDELIMETERA( *pPrevChar ) ||
*pPrevChar == 0x0A ||
ECIsDBCSLeadByte(ped, *pWordMinSel) ||
pWordMinSel - pPrevChar == 2 ) ) {
/*
* If we are looking for the start of the word right, then we
* stop when we have found it. (needed in case charLocated is
* still FALSE)
*/
break;
}
if ( pWordMinSel - pPrevChar == 2 ) {
/*
** previous character is a double byte character.
** if we are in a word ( charLocated == TRUE )
** current position is the beginning of the word
** if we are not in a word ( charLocated == FALSE )
** the previous character is what we looking for.
*/
if ( ! charLocated ) {
pWordMinSel = pPrevChar;
}
break;
}
pWordMinSel = pPrevChar;
if (!ISDELIMETERA(*pWordMinSel) && *pWordMinSel != 0x0A) {
/*
* We have found the last char in the word. Continue looking
* backwards till we find the first char of the word
*/
charLocated = TRUE;
/*
* We will consider a CR the start of a word
*/
if (*pWordMinSel == 0x0D)
break;
}
}
} else {
while ((ISDELIMETERA(*pWordMinSel) || *pWordMinSel == 0x0A) && pWordMinSel < pText + ped->cch)
pWordMinSel++;
}
/*
* Adjust the initial position of pWordMaxSel ( in ansi )
*/
pWordMaxSel = ECAnsiNext(ped, pWordMinSel);
pWordMaxSel = min(pWordMaxSel, pText + ped->cch);
/*
** If pWordMinSel points a double byte character AND
** pWordMaxSel points non space
** then
** pWordMaxSel points the beggining of next word.
*/
if ( ( pWordMaxSel - pWordMinSel == 2 ) && ! ISDELIMETERA(*pWordMaxSel) )
goto FastReturnA;
if (*pWordMinSel == 0x0D) {
if (pWordMinSel > pText && *(pWordMinSel - 1) == 0x0D)
/* So that we can treat CRCRLF as one word also. */
pWordMinSel--;
else if (*(pWordMinSel + 1) == 0x0D)
/* Move MaxSel on to the LF */
pWordMaxSel++;
}
/*
* Check if we have a one character word
*/
if (ISDELIMETERA(*pWordMaxSel))
spaceLocated = TRUE;
/*
* Move pWordMaxSel to the right looking for the end of a word and its
* trailing spaces. WordMaxSel stops on the first character of the next
* word. Thus, we break either at a CR or at the first nonspace char after
* a run of spaces or LFs.
*/
while ((pWordMaxSel < pText + ped->cch) && (!spaceLocated || (ISDELIMETERA(*pWordMaxSel)))) {
if (*pWordMaxSel == 0x0D)
break;
/*
* Treat double byte character as a word ( in ansi pWordMaxSel loop )
*/
/*
** if it's a double byte character then
** we are at the beginning of next word
** which is a double byte character.
*/
if (ECIsDBCSLeadByte( ped, *pWordMaxSel))
break;
pWordMaxSel++;
if (ISDELIMETERA(*pWordMaxSel))
spaceLocated = TRUE;
if (*(pWordMaxSel - 1) == 0x0A)
break;
}
/*
* label for fast return ( for Ansi )
*/
FastReturnA:
ECUnlock(ped);
if (pichMin) *pichMin = (ICH)(pWordMinSel - pText);
if (pichMax) *pichMax = (ICH)(pWordMaxSel - pText);
return;
} else { // !fAnsi
LPWSTR pwText;
LPWSTR pwWordMinSel;
LPWSTR pwWordMaxSel;
BOOL charLocated = FALSE;
BOOL spaceLocated = FALSE;
PWSTR pwPrevChar;
UserAssert(ped->cbChar == sizeof(WCHAR));
if (ped->lpfnNextWord) {
NextWordCallBack(ped, ichStart, fLeft, pichMin, pichMax);
return;
}
if (ped->pLpkEditCallout) {
NextWordLpkCallBack(ped, ichStart, fLeft, pichMin, pichMax);
return;
}
pwText = (LPWSTR)ECLock(ped);
pwWordMinSel = pwWordMaxSel = pwText + ichStart;
/*
* if fLeft: Move pWordMinSel to the left looking for the start of a word.
* If we start at a space, we will include spaces in the selection as we
* move left untill we find a nonspace character. At that point, we continue
* looking left until we find a space. Thus, the selection will consist of
* a word with its trailing spaces or, it will consist of any leading at the
* beginning of a line of text.
*/
/*
* if !fLeft: (ie. right word) Move pWordMinSel looking for the start of a
* word. If the pWordMinSel points to a character, then we move left
* looking for a space which will signify the start of the word. If
* pWordMinSel points to a space, we look right till we come upon a
* character. pMaxWord will look right starting at pMinWord looking for the
* end of the word and its trailing spaces.
*/
if (fLeft || (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0D))
/* If we are moving left or if we are moving right and we are not on a
* space or a CR (the start of a word), then we was look left for the
* start of a word which is either a CR or a character. We do this by
* looking left till we find a character (or if CR we stop), then we
* continue looking left till we find a space or LF.
*/ {
while (pwWordMinSel > pwText && ((!ISDELIMETERW(*(pwWordMinSel - 1)) && *(pwWordMinSel - 1) != 0x0A) || !charLocated)) {
/*
* Treat double byte character as a word ( in unicode pwWordMinSel loop )
*/
pwPrevChar = pwWordMinSel - 1;
/*
** we are looking right ( !fLeft ).
**
** if current character is a double width chararacter
** or previous character is a double width character,
** we are on the beggining of a word.
*/
if (!fLeft && (ISDELIMETERW( *pwPrevChar) ||
*pwPrevChar == 0x0A ||
UserIsFullWidth(CP_ACP,*pwWordMinSel) ||
UserIsFullWidth(CP_ACP,*pwPrevChar))) {
/*
* If we are looking for the start of the word right, then we
* stop when we have found it. (needed in case charLocated is
* still FALSE)
*/
break;
}
if (UserIsFullWidth(CP_ACP,*pwPrevChar)) {
/*
** Previous character is a double width character.
**
** if we are in a word ( charLocated == TRUE )
** current position is the beginning of the word
** if we are not in a word ( charLocated == FALSE )
** the previous character is what we looking for.
*/
if ( ! charLocated ) {
pwWordMinSel = pwPrevChar;
}
break;
}
pwWordMinSel = pwPrevChar;
if (!ISDELIMETERW(*pwWordMinSel) && *pwWordMinSel != 0x0A)
/*
* We have found the last char in the word. Continue looking
* backwards till we find the first char of the word
*/ {
charLocated = TRUE;
/*
* We will consider a CR the start of a word
*/
if (*pwWordMinSel == 0x0D)
break;
}
}
} else {
/*
* We are moving right and we are in between words so we need to move
* right till we find the start of a word (either a CR or a character.
*/
while ((ISDELIMETERW(*pwWordMinSel) || *pwWordMinSel == 0x0A) && pwWordMinSel < pwText + ped->cch)
pwWordMinSel++;
}
pwWordMaxSel = min((pwWordMinSel + 1), (pwText + ped->cch));
/*
** If pwWordMinSel points a double width character AND
** pwWordMaxSel points non space
** then
** pwWordMaxSel points the beggining of next word.
*/
if (UserIsFullWidth(CP_ACP,*pwWordMinSel) && ! ISDELIMETERW(*pwWordMaxSel))
goto FastReturnW;
if (*pwWordMinSel == 0x0D) {
if (pwWordMinSel > pwText && *(pwWordMinSel - 1) == 0x0D)
/* So that we can treat CRCRLF as one word also. */
pwWordMinSel--;
else if (*(pwWordMinSel + 1) == 0x0D)
/* Move MaxSel on to the LF */
pwWordMaxSel++;
}
/*
* Check if we have a one character word
*/
if (ISDELIMETERW(*pwWordMaxSel))
spaceLocated = TRUE;
/*
* Move pwWordMaxSel to the right looking for the end of a word and its
* trailing spaces. WordMaxSel stops on the first character of the next
* word. Thus, we break either at a CR or at the first nonspace char after
* a run of spaces or LFs.
*/
while ((pwWordMaxSel < pwText + ped->cch) && (!spaceLocated || (ISDELIMETERW(*pwWordMaxSel)))) {
if (*pwWordMaxSel == 0x0D)
break;
/*
* treat double byte character as a word ( in unicode pwWordMaxSel loop )
*/
/*
** if it's a double width character
** then we are at the beginning of
** the next word which is a double
** width character.
*/
if (UserIsFullWidth(CP_ACP,*pwWordMaxSel))
break;
pwWordMaxSel++;
if (ISDELIMETERW(*pwWordMaxSel))
spaceLocated = TRUE;
if (*(pwWordMaxSel - 1) == 0x0A)
break;
}
/*
* label for fast return ( for Unicode )
*/
FastReturnW:
ECUnlock(ped);
if (pichMin) *pichMin = (ICH)(pwWordMinSel - pwText);
if (pichMax) *pichMax = (ICH)(pwWordMaxSel - pwText);
return;
}
}
/***************************************************************************\
*
* ECSaveUndo() -
*
* Saves old undo information into given buffer, and clears out info in
* passed in undo buffer. If we're restoring, pundoFrom and pundoTo are
* reversed.
*
\***************************************************************************/
void ECSaveUndo(PUNDO pundoFrom, PUNDO pundoTo, BOOL fClear)
{
/*
* Save undo data
*/
RtlCopyMemory(pundoTo, pundoFrom, sizeof(UNDO));
/*
* Clear passed in undo buffer
*/
if (fClear)
RtlZeroMemory(pundoFrom, sizeof(UNDO) );
}
/***************************************************************************\
* ECEmptyUndo AorW
*
* empties the undo buffer.
*
* History:
\***************************************************************************/
void ECEmptyUndo(
PUNDO pundo )
{
if (pundo->hDeletedText)
UserGlobalFree(pundo->hDeletedText);
RtlZeroMemory(pundo, sizeof(UNDO) );
}
/***************************************************************************\
*
* ECMergeUndoInsertInfo() -
*
* When an insert takes place, this function is called with the info about
* the new insertion (the insertion point and the count of chars inserted);
* This looks at the existing Undo info and merges the new new insert info
* with it.
*
\***************************************************************************/
void ECMergeUndoInsertInfo(PUNDO pundo, ICH ichInsert, ICH cchInsert) \
{
//
// If undo buffer is empty, just insert the new info as UNDO_INSERT
//
if (pundo->undoType == UNDO_NONE) {
pundo->undoType = UNDO_INSERT;
pundo->ichInsStart = ichInsert;
pundo->ichInsEnd = ichInsert+cchInsert;
} else if (pundo->undoType & UNDO_INSERT) {
//
// If there's already some undo insert info,
// try to merge the two.
//
if (pundo->ichInsEnd == ichInsert) // Check they are adjacent.
pundo->ichInsEnd += cchInsert; // if so, just concatenate.
else {
// The new insert is not contiguous with the old one.
UNDOINSERT:
//
// If there is some UNDO_DELETE info already here, check to see
// if the new insert takes place at a point different from where
// that deletion occurred.
//
if ((pundo->undoType & UNDO_DELETE) && (pundo->ichDeleted != ichInsert)) {
//
// User is inserting into a different point; So, let us
// forget any UNDO_DELETE info;
//
if (pundo->hDeletedText)
UserGlobalFree(pundo->hDeletedText);
pundo->hDeletedText = NULL;
pundo->ichDeleted = 0xFFFFFFFF;
pundo->undoType &= ~UNDO_DELETE;
}
// Since the old insert and new insert are not adjacent, let us
// forget everything about the old insert and keep just the new
// insert info as the UNDO_INSERT.
pundo->ichInsStart = ichInsert;
pundo->ichInsEnd = ichInsert + cchInsert;
pundo->undoType |= UNDO_INSERT;
}
} else if (pundo->undoType == UNDO_DELETE) {
// If there is some Delete Info already present go and handle it.
goto UNDOINSERT;
}
}
/***************************************************************************\
* ECInsertText AorW
*
* Adds cch characters from lpText into the ped->hText starting at
* ped->ichCaret. Returns TRUE if successful else FALSE. Updates
* ped->cchAlloc and ped->cch properly if additional memory was allocated or
* if characters were actually added. Updates ped->ichCaret to be at the end
* of the inserted text. min and maxsel are equal to ichcaret.
*
* History:
\***************************************************************************/
BOOL ECInsertText(
PED ped,
LPSTR lpText,
ICH* pcchInsert)
{
PSTR pedText;
PSTR pTextBuff;
LONG style;
HANDLE hTextCopy;
DWORD allocamt;
//
// If the last byte (lpText[cchInsert - 1]) is a DBCS leading byte
// we need to adjust it.
//
*pcchInsert = ECAdjustIch(ped, lpText, *pcchInsert);
if (!*pcchInsert)
return TRUE;
/*
* Do we already have enough memory??
*/
if (*pcchInsert >= (ped->cchAlloc - ped->cch)) {
/*
* Allocate what we need plus a little extra. Return FALSE if we are
* unsuccessful.
*/
allocamt = (ped->cch + *pcchInsert) * ped->cbChar;
allocamt += CCHALLOCEXTRA;
// if (!ped->fSingle) {
hTextCopy = LOCALREALLOC(ped->hText, allocamt, LHND, ped->hInstance, &lpText);
if (hTextCopy) {
ped->hText = hTextCopy;
} else {
return FALSE;
}
// } else {
// if (!LocalReallocSafe(ped->hText, allocamt, LHND, pped))
// return FALSE;
// }
ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
}
/*
* Ok, we got the memory. Now copy the text into the structure
*/
pedText = ECLock(ped);
if (ped->pLpkEditCallout) {
HDC hdc;
INT iResult;
hdc = ECGetEditDC (ped, TRUE);
iResult = ped->pLpkEditCallout->EditVerifyText (ped, hdc, pedText, ped->ichCaret, lpText, *pcchInsert);
ECReleaseEditDC (ped, hdc, TRUE);
if (iResult == 0) {
ECUnlock (ped);
return TRUE;
}
}
/*
* Get a pointer to the place where text is to be inserted
*/
pTextBuff = pedText + ped->ichCaret * ped->cbChar;
if (ped->ichCaret != ped->cch) {
/*
* We are inserting text into the middle. We have to shift text to the
* right before inserting new text.
*/
memmove(pTextBuff + *pcchInsert * ped->cbChar, pTextBuff, (ped->cch-ped->ichCaret) * ped->cbChar);
}
/*
* Make a copy of the text being inserted in the edit buffer.
* Use this copy for doing UPPERCASE/LOWERCASE ANSI/OEM conversions
* Fix for Bug #3406 -- 01/29/91 -- SANKAR --
*/
memmove(pTextBuff, lpText, *pcchInsert * ped->cbChar);
ped->cch += *pcchInsert;
/*
* Get the control's style
*/
style = ped->pwnd->style;
/*
* Do the Upper/Lower conversion
*/
if (style & ES_LOWERCASE) {
if (ped->fAnsi)
CharLowerBuffA((LPSTR)pTextBuff, *pcchInsert);
else
CharLowerBuffW((LPWSTR)pTextBuff, *pcchInsert);
} else {
if (style & ES_UPPERCASE) {
if (ped->fAnsi) {
CharUpperBuffA(pTextBuff, *pcchInsert);
} else {
CharUpperBuffW((LPWSTR)pTextBuff, *pcchInsert);
}
}
}
/*
* Do the OEM conversion
*/
if ((style & ES_OEMCONVERT) &&
// For backward compatibility with NT4, we don't perform OEM conversion
// for older apps if the system locale is FarEast.
//
(!IS_DBCS_ENABLED() || GETAPPVER() >= VER50 || GetOEMCP() != GetACP())) {
ICH i;
if (ped->fAnsi) {
for (i = 0; i < *pcchInsert; i++) {
//
// We don't need to call CharToOemBuff etc. if the character
// is a double byte character. And, calling ECIsDBCSLeadByte is
// faster and less complicated because we don't have to deal
// with the 2 byte dbcs cases.
//
if (IS_DBCS_ENABLED() && ECIsDBCSLeadByte(ped, *(lpText+i))) {
i++;
continue;
}
//
// Windows Bug (Whistler) 35289
// greek has funny rules for casing, so we need to check for it.
// for nashville we should be doing something more appropriate
// but for now, leave as Win95 golden
//
if (ped->charSet != GREEK_CHARSET && IsCharLowerA(*(pTextBuff + i))) {
CharUpperBuffA(pTextBuff + i, 1);
CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
CharLowerBuffA(pTextBuff + i, 1);
} else {
CharToOemBuffA(pTextBuff + i, pTextBuff + i, 1);
OemToCharBuffA(pTextBuff + i, pTextBuff + i, 1);
}
}
} else {
//
// Because 'ch' may become DBCS, and have a space for NULL.
//
UCHAR ch[4];
LPWSTR lpTextW = (LPWSTR)pTextBuff;
for (i = 0; i < *pcchInsert; i++) {
if (*(lpTextW + i) == UNICODE_CARRIAGERETURN ||
*(lpTextW + i) == UNICODE_LINEFEED ||
*(lpTextW + i) == UNICODE_TAB) {
continue;
}
//
// Windows Bug (Whistler) 35289
// greek has funny rules for casing, so we need to check for it.
// for nashville we should be doing something more appropriate
// but for now, leave as Win95 golden
//
if (ped->charSet != GREEK_CHARSET && IsCharLowerW(*(lpTextW + i))) {
CharUpperBuffW(lpTextW + i, 1);
*(LPDWORD)ch = 0; // make sure the null-terminate.
CharToOemBuffW(lpTextW + i, ch, 1);
//
// We assume any SBCS/DBCS character will converted
// to 1 Unicode char, Otherwise, we may overwrite
// next character...
//
OemToCharBuffW(ch, lpTextW + i, strlen(ch));
CharLowerBuffW(lpTextW + i, 1);
} else {
*(LPDWORD)ch = 0; // make sure the null-terminate.
CharToOemBuffW(lpTextW + i, ch, 1);
//
// We assume any SBCS/DBCS character will converted
// to 1 Unicode char, Otherwise, we may overwrite
// next character...
//
OemToCharBuffW(ch, lpTextW + i, strlen(ch));
}
}
}
}
/* Adjust UNDO fields so that we can undo this insert... */
ECMergeUndoInsertInfo(Pundo(ped), ped->ichCaret, *pcchInsert);
ped->ichCaret += *pcchInsert;
if (ped->pLpkEditCallout) {
HDC hdc;
hdc = ECGetEditDC (ped, TRUE);
ped->ichCaret = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichCaret);
ECReleaseEditDC (ped, hdc, TRUE);
}
ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;
ECUnlock(ped);
/*
* Set dirty bit
*/
ped->fDirty = TRUE;
return TRUE;
}
/***************************************************************************\
* ECDeleteText AorW
*
* Deletes the text between ped->ichMinSel and ped->ichMaxSel. The
* character at ichMaxSel is not deleted. But the character at ichMinSel is
* deleted. ped->cch is updated properly and memory is deallocated if enough
* text is removed. ped->ichMinSel, ped->ichMaxSel, and ped->ichCaret are set
* to point to the original ped->ichMinSel. Returns the number of characters
* deleted.
*
* History:
\***************************************************************************/
ICH ECDeleteText(
PED ped)
{
PSTR pedText;
ICH cchDelete;
LPSTR lpDeleteSaveBuffer;
HANDLE hDeletedText;
DWORD bufferOffset;
cchDelete = ped->ichMaxSel - ped->ichMinSel;
if (!cchDelete)
return (0);
/*
* Ok, now lets delete the text.
*/
pedText = ECLock(ped);
/*
* Adjust UNDO fields so that we can undo this delete...
*/
if (ped->undoType == UNDO_NONE) {
UNDODELETEFROMSCRATCH:
if (ped->hDeletedText = UserGlobalAlloc(GPTR, (LONG)((cchDelete+1)*ped->cbChar))) {
ped->undoType = UNDO_DELETE;
ped->ichDeleted = ped->ichMinSel;
ped->cchDeleted = cchDelete;
lpDeleteSaveBuffer = ped->hDeletedText;
RtlCopyMemory(lpDeleteSaveBuffer, pedText + ped->ichMinSel*ped->cbChar, cchDelete*ped->cbChar);
lpDeleteSaveBuffer[cchDelete*ped->cbChar] = 0;
}
} else if (ped->undoType & UNDO_INSERT) {
UNDODELETE:
ECEmptyUndo(Pundo(ped));
ped->ichInsStart = ped->ichInsEnd = 0xFFFFFFFF;
ped->ichDeleted = 0xFFFFFFFF;
ped->cchDeleted = 0;
goto UNDODELETEFROMSCRATCH;
} else if (ped->undoType == UNDO_DELETE) {
if (ped->ichDeleted == ped->ichMaxSel) {
/*
* Copy deleted text to front of undo buffer
*/
hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
if (!hDeletedText)
goto UNDODELETE;
bufferOffset = 0;
ped->ichDeleted = ped->ichMinSel;
} else if (ped->ichDeleted == ped->ichMinSel) {
/*
* Copy deleted text to end of undo buffer
*/
hDeletedText = UserGlobalReAlloc(ped->hDeletedText, (LONG)(cchDelete + ped->cchDeleted + 1)*ped->cbChar, GHND);
if (!hDeletedText)
goto UNDODELETE;
bufferOffset = ped->cchDeleted*ped->cbChar;
} else {
/*
* Clear the current UNDO delete and add the new one since
the deletes aren't contiguous.
*/
goto UNDODELETE;
}
ped->hDeletedText = hDeletedText;
lpDeleteSaveBuffer = (LPSTR)hDeletedText;
if (!bufferOffset) {
/*
* Move text in delete buffer up so that we can insert the next
* text at the head of the buffer.
*/
RtlMoveMemory(lpDeleteSaveBuffer + cchDelete*ped->cbChar, lpDeleteSaveBuffer,
ped->cchDeleted*ped->cbChar);
}
RtlCopyMemory(lpDeleteSaveBuffer + bufferOffset, pedText + ped->ichMinSel*ped->cbChar,
cchDelete*ped->cbChar);
lpDeleteSaveBuffer[(ped->cchDeleted + cchDelete)*ped->cbChar] = 0;
ped->cchDeleted += cchDelete;
}
if (ped->ichMaxSel != ped->cch) {
/*
* We are deleting text from the middle of the buffer so we have to
shift text to the left.
*/
RtlMoveMemory(pedText + ped->ichMinSel*ped->cbChar, pedText + ped->ichMaxSel*ped->cbChar,
(ped->cch - ped->ichMaxSel)*ped->cbChar);
}
if (ped->cchAlloc - ped->cch > CCHALLOCEXTRA) {
/*
* Free some memory since we deleted a lot
*/
LOCALREALLOC(ped->hText, (DWORD)(ped->cch + (CCHALLOCEXTRA / 2))*ped->cbChar, LHND, ped->hInstance, NULL);
ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
}
ped->cch -= cchDelete;
if (ped->pLpkEditCallout) {
HDC hdc;
hdc = ECGetEditDC (ped, TRUE);
ped->ichMinSel = ped->pLpkEditCallout->EditAdjustCaret (ped, hdc, pedText, ped->ichMinSel);
ECReleaseEditDC (ped, hdc, TRUE);
}
ped->ichCaret = ped->ichMaxSel = ped->ichMinSel;
ECUnlock(ped);
/*
* Set dirty bit
*/
ped->fDirty = TRUE;
return (cchDelete);
}
/***************************************************************************\
* ECNotifyParent AorW
*
* Sends the notification code to the parent of the edit control
*
* History:
\***************************************************************************/
void ECNotifyParent(
PED ped,
int notificationCode)
{
/*
* wParam is NotificationCode (hiword) and WindowID (loword)
* lParam is HWND of control sending the message
* Windows 95 checks for hwndParent != NULL before sending the message, but
* this is surely rare, and SendMessage NULL hwnd does nowt anyway (IanJa)
*/
SendMessage(ped->hwndParent, WM_COMMAND,
(DWORD)MAKELONG(PTR_TO_ID(ped->pwnd->spmenu), notificationCode),
(LPARAM)ped->hwnd);
}
/***************************************************************************\
*
* ECSetEditClip() AorW
*
* Sets the clip rect for the hdc to the formatting rectangle intersected
* with the client area.
*
\***************************************************************************/
void ECSetEditClip(PED ped, HDC hdc, BOOL fLeftMargin)
{
RECT rcClient;
RECT rcClip;
CopyRect(&rcClip, &ped->rcFmt);
if (ped->pLpkEditCallout) {
// Complex script handling chooses whether to write margins later
rcClip.left -= ped->wLeftMargin;
rcClip.right += ped->wRightMargin;
} else {
if (fLeftMargin) /* Should we consider the left margin? */
rcClip.left -= ped->wLeftMargin;
if (ped->fWrap) /* Should we consider the right margin? */
rcClip.right += ped->wRightMargin;
}
/* Set clip rectangle to rectClient intersect rectClip */
/* We must clip for single line edits also. -- B#1360 */
_GetClientRect(ped->pwnd, &rcClient);
if (ped->fFlatBorder)
InflateRect(&rcClient, -SYSMET(CXBORDER), -SYSMET(CYBORDER));
IntersectRect(&rcClient, &rcClient, &rcClip);
IntersectClipRect(hdc,rcClient.left, rcClient.top,
rcClient.right, rcClient.bottom);
}
/***************************************************************************\
* ECGetEditDC AorW
*
* Hides the caret, gets the DC for the edit control, and clips to
* the rcFmt rectangle specified for the edit control and sets the proper
* font. If fFastDC, just select the proper font but don't bother about clip
* regions or hiding the caret.
*
* History:
\***************************************************************************/
HDC ECGetEditDC(
PED ped,
BOOL fFastDC )
{
HDC hdc;
if (!fFastDC)
NtUserHideCaret(ped->hwnd);
if ( hdc = NtUserGetDC(ped->hwnd) ) {
ECSetEditClip(ped, hdc, (BOOL)(ped->xOffset == 0));
/*
* Select the proper font for this edit control's dc.
*/
if (ped->hFont)
SelectObject(hdc, ped->hFont);
}
return hdc;
}
/***************************************************************************\
* ECReleaseEditDC AorW
*
* Releases the DC (hdc) for the edit control and shows the caret.
* If fFastDC, just select the proper font but don't bother about showing the
* caret.
*
* History:
\***************************************************************************/
void ECReleaseEditDC(
PED ped,
HDC hdc,
BOOL fFastDC)
{
/*
* Restoring font not necessary
*/
ReleaseDC(ped->hwnd, hdc);
if (!fFastDC)
NtUserShowCaret(ped->hwnd);
}
/***************************************************************************\
*
* ECResetTextInfo() AorW
*
* Handles a global change to the text by resetting text offsets, emptying
* the undo buffer, and rebuilding the lines
*
\***************************************************************************/
void ECResetTextInfo(PED ped)
{
//
// Reset caret, selections, scrolling, and dirty information.
//
ped->iCaretLine = ped->ichCaret = 0;
ped->ichMinSel = ped->ichMaxSel = 0;
ped->xOffset = ped->ichScreenStart = 0;
ped->fDirty = FALSE;
ECEmptyUndo(Pundo(ped));
if (ped->fSingle) {
if (!ped->listboxHwnd)
ECNotifyParent(ped, EN_UPDATE);
} else {
#ifdef BOGUS
// B#14640
// We don't want to strip soft breaks or anything else from text
// that was passed in by the caller. - karlst.
MLStripCrCrLf(ped);
#endif
MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
}
if (_IsWindowVisible(ped->pwnd)) {
BOOL fErase;
if (ped->fSingle)
fErase = FALSE;
else
fErase = ((ped->ichLinesOnScreen + ped->ichScreenStart) >= ped->cLines);
// Always redraw whether or not the insert was successful. We might
// have NULL text. Paint() will check the redraw flag for us.
ECInvalidateClient(ped, fErase);
// BACKWARD COMPAT HACK: RAID expects the text to have been updated,
// so we have to do an UpdateWindow here. It moves an edit control
// around with fRedraw == FALSE, so it'll never get the paint message
// with the control in the right place.
if (!ped->fWin31Compat)
UpdateWindow(ped->hwnd);
}
if (ped->fSingle && !ped->listboxHwnd) {
ECNotifyParent(ped, EN_CHANGE);
}
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, HW(ped->pwnd), OBJID_CLIENT, INDEXID_CONTAINER);
}
/***************************************************************************\
* ECSetText AorW
*
* Copies the null terminated text in lpstr to the ped. Notifies the
* parent if there isn't enough memory. Sets the minsel, maxsel, and caret to
* the beginning of the inserted text. Returns TRUE if successful else FALSE
* if no memory (and notifies the parent).
*
* History:
\***************************************************************************/
BOOL ECSetText(
PED ped,
LPSTR lpstr)
{
ICH cchLength;
ICH cchSave = ped->cch;
ICH ichCaretSave = ped->ichCaret;
HWND hwndSave = ped->hwnd;
HANDLE hText;
ped->cch = ped->ichCaret = 0;
ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
if (!lpstr) {
hText = LOCALREALLOC(ped->hText, CCHALLOCEXTRA*ped->cbChar, LHND, ped->hInstance, &lpstr);
if (hText != NULL) {
ped->hText = hText;
} else {
return FALSE;
}
} else {
cchLength = StringLength(lpstr, ped->fAnsi);
#ifdef NEVER
// win3.1 does limit single line edit controls to 32K (minus 3) but NT doesn't
if (ped->fSingle) {
/*
* Limit single line edit controls to 32K
*/
cchLength = min(cchLength, (ICH)(0x7FFD/ped->cbChar));
}
#endif
/*
* Add the text
*/
if (cchLength && !ECInsertText(ped, lpstr, &cchLength)) {
/*
* Restore original state and notify parent we ran out of memory.
*/
ped->cch = cchSave;
ped->ichCaret = ichCaretSave;
ECNotifyParent(ped, EN_ERRSPACE);
return FALSE;
}
}
ped->cchAlloc = LOCALSIZE(ped->hText, ped->hInstance) / ped->cbChar;
if (IsWindow(hwndSave))
ECResetTextInfo(ped);
return TRUE;
}
/***************************************************************************\
*
* ECInvalidateClient()
*
* Invalidates client of edit field. For old 3.x guys with borders,
* we draw it ourself (compatibility). So we don't want to invalidate
* the border or we'll get flicker.
*
\***************************************************************************/
void ECInvalidateClient(PED ped, BOOL fErase)
{
if (ped->fFlatBorder) {
RECT rcT;
_GetClientRect(ped->pwnd, &rcT);
InflateRect(&rcT, -SYSMET(CXBORDER),
-SYSMET(CYBORDER));
NtUserInvalidateRect(ped->hwnd, &rcT, fErase);
} else {
NtUserInvalidateRect(ped->hwnd, NULL, fErase);
}
}
/***************************************************************************\
* ECCopy AorW
*
* Copies the text between ichMinSel and ichMaxSel to the clipboard.
* Returns the number of characters copied.
*
* History:
\***************************************************************************/
ICH ECCopy(
PED ped)
{
HANDLE hData;
char *pchSel;
char FAR *lpchClip;
ICH cbData;
/*
* Don't allow copies from password style controls
*/
if (ped->charPasswordChar) {
NtUserMessageBeep(0);
return 0;
}
cbData = (ped->ichMaxSel - ped->ichMinSel) * ped->cbChar;
if (!cbData)
return 0;
if (!OpenClipboard(ped->hwnd))
return 0;
NtUserEmptyClipboard();
/*
* If we just called EmptyClipboard in the context of a 16 bit
* app then we also have to tell WOW to nix its 16 handle copy of
* clipboard data. WOW does its own clipboard caching because
* some 16 bit apps use clipboard data even after the clipboard
* has been emptied. See the note in the server code.
*
* Note: this is the only place where EmptyClipboard is called
* for a 16 bit app not going through WOW. If we added others
* we might want to move this into EmptyClipboard and have two
* versions.
*/
if (GetClientInfo()->CI_flags & CI_16BIT) {
pfnWowEmptyClipBoard();
}
/*
* +1 for the terminating NULL
*/
if (!(hData = UserGlobalAlloc(LHND, (LONG)(cbData + ped->cbChar)))) {
NtUserCloseClipboard();
return (0);
}
USERGLOBALLOCK(hData, lpchClip);
UserAssert(lpchClip);
pchSel = ECLock(ped);
pchSel = pchSel + (ped->ichMinSel * ped->cbChar);
RtlCopyMemory(lpchClip, pchSel, cbData);
if (ped->fAnsi)
*(lpchClip + cbData) = 0;
else
*(LPWSTR)(lpchClip + cbData) = (WCHAR)0;
ECUnlock(ped);
USERGLOBALUNLOCK(hData);
SetClipboardData( ped->fAnsi ? CF_TEXT : CF_UNICODETEXT, hData);
NtUserCloseClipboard();
return (cbData);
}
/***************************************************************************\
* EditWndProcA
*
* Always receives Ansi messages and translates them if appropriate to unicode
* depending on the PED type
*
*
\***************************************************************************/
LRESULT EditWndProcA(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return 0;
/*
* If the control is not interested in this message,
* pass it to DefWindowProc.
*/
if (!FWINDOWMSG(message, FNID_EDIT))
return DefWindowProcWorker(pwnd, message, wParam, lParam, TRUE);
return EditWndProcWorker(pwnd, message, wParam, lParam, TRUE);
}
LRESULT EditWndProcW(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PWND pwnd;
if ((pwnd = ValidateHwnd(hwnd)) == NULL)
return 0;
/*
* If the control is not interested in this message,
* pass it to DefWindowProc.
*/
if (!FWINDOWMSG(message, FNID_EDIT)) {
return DefWindowProcWorker(pwnd, message, wParam, lParam, FALSE);
}
return EditWndProcWorker(pwnd, message, wParam, lParam, FALSE);
}
LRESULT EditWndProcWorker(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam,
DWORD fAnsi)
{
PED ped;
HWND hwnd = HWq(pwnd);
static BOOL fInit = TRUE;
VALIDATECLASSANDSIZE(pwnd, FNID_EDIT);
INITCONTROLLOOKASIDE(&EditLookaside, ED, pwnd, 4);
/*
* Get the ped for the given window now since we will use it a lot in
* various handlers. This was stored using SetWindowLong(hwnd,0,hped) when
* we initially created the edit control.
*/
ped = ((PEDITWND)pwnd)->ped;
/*
* Make sure the ANSI flag is set correctly.
*/
if (!ped->fInitialized) {
ped->fInitialized = TRUE;
ped->fAnsi = TestWF(pwnd, WFANSICREATOR) ? TRUE : FALSE;
}
/*
* We just call the regular EditWndProc if the ped is not created, the
* incoming message type already matches the PED type or the message
* does not need any translation.
*/
if (ped->fAnsi == fAnsi ||
(message >= WM_USER) ||
!MessageTable[message].bThunkMessage) {
return EditWndProc(pwnd, message, wParam, lParam);
}
return CsSendMessage(hwnd, message, wParam, lParam,
fAnsi ? (ULONG_PTR)EditWndProcW : (ULONG_PTR)EditWndProcA,
FNID_CALLWINDOWPROC, fAnsi);
}
/***************************************************************************\
* EditWndProc
*
* Class procedure for all edit controls.
* Dispatches all messages to the appropriate handlers which are named
* as follows:
* SL (single line) prefixes all single line edit control procedures while
* ML (multi line) prefixes all multi- line edit controls.
* EC (edit control) prefixes all common handlers.
*
* The EditWndProc only handles messages common to both single and multi
* line edit controls. Messages which are handled differently between
* single and multi are sent to SLEditWndProc or MLEditWndProc.
*
* Top level procedures are EditWndPoc, SLEditWndProc, and MLEditWndProc.
* SL*Handler or ML*Handler or EC*Handler procs are called to handle
* the various messages. Support procedures are prefixed with SL ML or
* EC depending on which code they support. They are never called
* directly and most assumptions/effects are documented in the effects
* clause.
*
* WARNING: If you add a message here, add it to gawEditWndProc[] in
* kernel\server.c too, otherwise EditWndProcA/W will send it straight to
* DefWindowProcWorker
*
* History:
\***************************************************************************/
LRESULT EditWndProc(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HWND hwnd = HWq(pwnd);
LRESULT lreturn;
PED ped;
/*
* Get the ped for the given window now since we will use it a lot in
* various handlers. This was stored using SetWindowLong(hwnd,0,hped) when
* we initially created the edit control.
*/
ped = ((PEDITWND)pwnd)->ped;
/*
* Dispatch the various messages we can receive
*/
lreturn = 1L;
switch (message) {
/*
* Messages which are handled the same way for both single and multi line
* edit controls.
*/
case WM_KEYDOWN:
// LPK handling of Ctrl/LShift, Ctrl/RShift
if (ped && ped->pLpkEditCallout && ped->fAllowRTL) {
ped->fSwapRoOnUp = FALSE; // Any keydown cancels a ctrl/shift reading order change
switch (wParam) {
case VK_SHIFT:
if ((GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000)) {
// Left shift or right shift pressed while control held down
// Check that alt (VK_MENU) isn't down to avoid false firing on AltGr which equals Ctrl+Alt.
if (MapVirtualKey((LONG)lParam>>16&0xff, 3) == VK_LSHIFT) {
// User wants left to right reading order
ped->fSwapRoOnUp = (ped->fRtoLReading) || (ped->format & ES_RIGHT) ;
ped->fLShift = TRUE;
} else {
// User wants right to left reading order
ped->fSwapRoOnUp = (!ped->fRtoLReading) || (ped->format & ES_RIGHT);
ped->fLShift = FALSE;
}
}
break;
case VK_LEFT:
if (ped->fRtoLReading) {
wParam = VK_RIGHT;
}
break;
case VK_RIGHT:
if (ped->fRtoLReading) {
wParam = VK_LEFT;
}
break;
}
}
goto HandleEditMsg;
case WM_KEYUP:
if (ped && ped->pLpkEditCallout && ped->fAllowRTL && ped->fSwapRoOnUp) {
BOOL fReadingOrder;
// Complete reading order change detected earlier during keydown
ped->fSwapRoOnUp = FALSE;
fReadingOrder = ped->fRtoLReading;
// Remove any overriding ES_CENTRE or ES_RIGHT format from dwStyle
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~ES_FMTMASK);
if (ped->fLShift) {
// Set Left to Right reading order and right scrollbar in EX_STYLE
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE)
& ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
// Edit control is LTR now, then notify the parent.
ECNotifyParent(ped, EN_ALIGN_LTR_EC);
// ? Select a keyboard layout appropriate to LTR operation
} else {
// Set Right to Left reading order, right alignment and left scrollbar
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE)
| WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
// Edit control is RTL now, then notify the parent.
ECNotifyParent(ped, EN_ALIGN_RTL_EC);
// ? Select a keyboard layout appropriate to RTL operation
}
// If reading order didn't change, so we are sure the alignment changed and the edit window didn't invalidate yet.
if (fReadingOrder == (BOOL) ped->fRtoLReading) {
ECInvalidateClient(ped, TRUE);
}
}
goto HandleEditMsg;
case WM_INPUTLANGCHANGE:
if (ped) {
// EC_INSERT_COMPOSITION_CHAR : WM_INPUTLANGCHANGE - call ECInitInsert()
HKL hkl = THREAD_HKL();
ECInitInsert(ped, hkl);
if (ped->fInReconversion) {
ECInOutReconversionMode(ped, FALSE);
}
//
// Font and caret position might be changed while
// another keyboard layout is active. Set those
// if the edit control has the focus.
//
if (ped->fFocus && fpImmIsIME(hkl)) {
POINT pt;
ECImmSetCompositionFont(ped);
NtUserGetCaretPos(&pt);
ECImmSetCompositionWindow(ped, pt.x, pt.y);
}
}
goto HandleEditMsg;
case WM_COPY:
/*
* wParam - not used
* lParam - not used
*/
lreturn = (LONG)ECCopy(ped);
break;
case WM_CUT:
/*
*
* wParamLo -- unused
* lParam -- unused
*/
ECCutText(ped);
return 0;
case WM_CLEAR:
/*
* wParamLo -- unused
* lParam -- unused
*/
ECClearText(ped);
return 0;
case WM_ENABLE:
/*
* wParam - nonzero if window is enabled else disable window if 0.
* lParam - not used
*/
lreturn = (LONG)(ped->fDisabled = !((BOOL)wParam));
ECInvalidateClient(ped, TRUE);
break;
case WM_SYSCHAR:
//
// wParamLo -- key value
// lParam -- unused
//
//
// If this is a WM_SYSCHAR message generated by the UNDO
// keystroke we want to EAT IT
//
if ((lParam & SYS_ALTERNATE) && ((WORD)wParam == VK_BACK))
return TRUE;
else {
return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
}
break;
case EM_GETLINECOUNT:
/*
* wParam - not used
lParam - not used
*/
lreturn = (LONG)ped->cLines;
break;
case EM_GETMODIFY:
/*
* wParam - not used
lParam - not used
*/
/*
* Gets the state of the modify flag for this edit control.
*/
lreturn = (LONG)ped->fDirty;
break;
case EM_SETMODIFY:
/*
* wParam - specifies the new value for the modify flag
lParam - not used
*/
/*
* Sets the state of the modify flag for this edit control.
*/
ped->fDirty = (wParam != 0);
break;
case EM_GETRECT:
/*
* wParam - not used
lParam - pointer to a RECT data structure that gets the dimensions.
*/
/*
* Copies the rcFmt rect to *lpRect.
*/
CopyRect((LPRECT)lParam, (LPRECT)&ped->rcFmt);
lreturn = (LONG)TRUE;
break;
case WM_GETFONT:
/*
* wParam - not used
lParam - not used
*/
lreturn = (LRESULT)ped->hFont;
break;
case WM_SETFONT:
/*
* wParam - handle to the font
lParam - redraw if true else don't
*/
ECSetFont(ped, (HANDLE)wParam, (BOOL)LOWORD(lParam));
break;
case WM_GETTEXT:
/*
* wParam - max number of _bytes_ (not characters) to copy
* lParam - buffer to copy text to. Text is 0 terminated.
*/
lreturn = (LRESULT)ECGetText(ped, (ICH)wParam, (LPSTR)lParam, TRUE);
break;
case WM_SETTEXT:
//
// wParamLo -- unused
// lParam -- LPSTR, null-terminated, with new text.
//
lreturn = (LRESULT)ECSetText(ped, (LPSTR)lParam);
break;
case WM_GETTEXTLENGTH:
/*
* Return count of CHARs!!!
*/
lreturn = (LONG)ped->cch;
break;
case WM_NCDESTROY:
case WM_FINALDESTROY:
/*
* wParam - not used
lParam - not used
*/
ECNcDestroyHandler(pwnd, ped);
return 0;
/*
* Most apps (i.e. everyone but Quicken) don't pass on the rbutton
* messages when they do something with 'em inside of subclassed
* edit fields. As such, we keep track of whether we saw the
* down before the up. If we don't see the up, then DefWindowProc
* won't generate the context menu message, so no big deal. If
* we didn't see the down, then don't let WM_CONTEXTMENU do
* anything.
*
* We also might want to not generate WM_CONTEXTMENUs for old
* apps when the mouse is captured.
*/
case WM_RBUTTONDOWN:
ped->fSawRButtonDown = TRUE;
goto HandleEditMsg;
case WM_RBUTTONUP:
if (ped->fSawRButtonDown) {
ped->fSawRButtonDown = FALSE;
if (!ped->fInReconversion) {
goto HandleEditMsg;
}
}
// Don't pass this on to DWP so WM_CONTEXTMENU isn't generated.
return 0;
case WM_CONTEXTMENU: {
POINT pt ;
int nHit = FindNCHit(pwnd, (LONG)lParam);
if ((nHit == HTVSCROLL) || (nHit == HTHSCROLL)) {
return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
}
POINTSTOPOINT(pt, lParam);
if (!TestWF(pwnd, WFOLDUI) && ECIsAncestorActive(hwnd))
ECMenu(hwnd, ped, &pt);
}
return 0;
case EM_CANUNDO:
/*
* wParam - not used
lParam - not used
*/
lreturn = (LONG)(ped->undoType != UNDO_NONE);
break;
case EM_EMPTYUNDOBUFFER:
/*
* wParam - not used
lParam - not used
*/
ECEmptyUndo(Pundo(ped));
break;
case EM_GETMARGINS:
//
// wParam -- unused
// lParam -- unused
//
return(MAKELONG(ped->wLeftMargin, ped->wRightMargin));
case EM_SETMARGINS:
//
// wParam -- EC_ margin flags
// lParam -- LOWORD is left, HIWORD is right margin
//
ECSetMargin(ped, (UINT)wParam, (DWORD)lParam, TRUE);
return 0;
case EM_GETSEL:
/*
* Gets the selection range for the given edit control. The
* starting position is in the low order word. It contains the position
* of the first nonselected character after the end of the selection in
* the high order word.
*/
if ((PDWORD)wParam != NULL) {
*((PDWORD)wParam) = ped->ichMinSel;
}
if ((PDWORD)lParam != NULL) {
*((PDWORD)lParam) = ped->ichMaxSel;
}
lreturn = MAKELONG(ped->ichMinSel,ped->ichMaxSel);
break;
case EM_GETLIMITTEXT:
//
// wParamLo -- unused
// lParam -- unused
//
return(ped->cchTextMax);
case EM_SETLIMITTEXT: /* Renamed from EM_LIMITTEXT in Chicago */
/*
* wParam - max number of CHARACTERS that can be entered
* lParam - not used
*/
/*
* Specifies the maximum number of characters of text the user may
* enter. If maxLength is 0, we may enter MAXINT number of CHARACTERS.
*/
if (ped->fSingle) {
if (wParam) {
wParam = min(0x7FFFFFFEu, wParam);
} else {
wParam = 0x7FFFFFFEu;
}
}
if (wParam) {
ped->cchTextMax = (ICH)wParam;
} else {
ped->cchTextMax = 0xFFFFFFFFu;
}
break;
case EM_POSFROMCHAR:
//
// Validate that char index is within text range
//
if (wParam >= ped->cch) {
return(-1L);
}
goto HandleEditMsg;
case EM_CHARFROMPOS: {
// Validate that point is within client of edit field
RECT rc;
POINT pt;
POINTSTOPOINT(pt, lParam);
_GetClientRect(pwnd, &rc);
if (!PtInRect(&rc, pt)) {
return(-1L);
}
goto HandleEditMsg;
}
case EM_SETPASSWORDCHAR:
/*
* wParam - sepecifies the new char to display instead of the
* real text. if null, display the real text.
*/
ECSetPasswordChar(ped, (UINT)wParam);
break;
case EM_GETPASSWORDCHAR:
lreturn = (DWORD)ped->charPasswordChar;
break;
case EM_SETREADONLY:
/*
* wParam - state to set read only flag to
*/
ped->fReadOnly = (wParam != 0);
if (wParam)
SetWindowState(pwnd, EFREADONLY);
else
ClearWindowState(pwnd, EFREADONLY);
lreturn = 1L;
ECEnableDisableIME( ped );
// We need to redraw the edit field so that the background color
// changes. Read-only edits are drawn in CTLCOLOR_STATIC while
// others are drawn with CTLCOLOR_EDIT.
ECInvalidateClient(ped, TRUE);
break;
case EM_SETWORDBREAKPROC:
/*
* wParam - unused
* lParam - FARPROC address of an app supplied call back function
*/
ped->lpfnNextWord = (EDITWORDBREAKPROCA)lParam;
break;
case EM_GETWORDBREAKPROC:
lreturn = (LRESULT)ped->lpfnNextWord;
break;
// IME
case EM_GETIMESTATUS:
// wParam == sub command
switch (wParam) {
case EMSIS_COMPOSITIONSTRING:
return ped->wImeStatus;
#if 0 // memphis
case EMSIS_GETLBBIT:
return (DWORD)ped->bLBBit;
#endif
}
break;
case EM_SETIMESTATUS:
// wParam == sub command
switch (wParam) {
case EMSIS_COMPOSITIONSTRING:
ped->wImeStatus = (WORD)lParam;
}
break;
case WM_NCCREATE:
lreturn = ECNcCreate(ped, pwnd, (LPCREATESTRUCT)lParam);
break;
case WM_LBUTTONDOWN:
//
// B#3623
// Don't set focus to edit field if it is within an inactive,
// captioned child.
// We might want to version switch this... I haven't found
// any problems by not, but you never know...
//
if (ECIsAncestorActive(hwnd)) {
/*
* Reconversion support: quit reconversion if left button is clicked.
* Otherwise, if the current KL is Korean, finailize the composition string.
*/
if (ped->fInReconversion || ped->fKorea) {
BOOLEAN fReconversion = (BOOLEAN)ped->fInReconversion;
DWORD dwIndex = fReconversion ? CPS_CANCEL : CPS_COMPLETE;
HIMC hImc;
ped->fReplaceCompChr = FALSE;
hImc = fpImmGetContext(ped->hwnd);
if (hImc) {
fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, dwIndex, 0);
fpImmReleaseContext(ped->hwnd, hImc);
}
if (fReconversion) {
ECInOutReconversionMode(ped, FALSE);
}
ECSetCaretHandler(ped);
}
goto HandleEditMsg;
}
break;
case WM_MOUSEMOVE:
//
// We only care about mouse messages when mouse is down.
//
if (ped->fMouseDown)
goto HandleEditMsg;
break;
case WM_IME_SETCONTEXT:
//
// If ped->fInsertCompChr is TRUE, that means we will do
// all the composition character drawing by ourself.
//
if ( ped->fInsertCompChr ) {
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
}
if ( wParam ) {
PINPUTCONTEXT pInputContext;
HIMC hImc;
hImc = fpImmGetContext( hwnd );
if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) {
pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS;
fpImmUnlockIMC( hImc );
}
if (GetClientInfo()->CI_flags & CI_16BIT) {
fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L);
}
fpImmReleaseContext( hwnd, hImc );
}
return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
case WM_IME_ENDCOMPOSITION:
ECInOutReconversionMode(ped, FALSE);
if (ped->fReplaceCompChr) {
ICH ich;
HDC hdc;
//
// we have a DBCS character to be replaced.
// let's delete it before inserting the new one.
//
ich = (ped->fAnsi) ? 2 : 1;
ped->fReplaceCompChr = FALSE;
ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch);
ped->ichMinSel = ped->ichCaret;
if (ped->fSingle) {
if (ECDeleteText( ped ) > 0) {
//
// Update the display
//
ECNotifyParent(ped, EN_UPDATE);
hdc = ECGetEditDC(ped, FALSE);
SLDrawText(ped, hdc, 0);
ECReleaseEditDC(ped, hdc, FALSE);
//
// Tell parent our text contents changed.
//
ECNotifyParent(ped, EN_CHANGE);
}
}
else {
MLDeleteText(ped);
}
ECSetCaretHandler( ped );
}
return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
case WM_IME_STARTCOMPOSITION:
if ( ped->fInsertCompChr ) {
//
// NOTE:
// sending WM_IME_xxxCOMPOSITION will let
// IME draw composition window. IME should
// not do that since we cleared
// ISC_SHOWUICOMPOSITIONWINDOW bit when
// we got WM_IME_SETCONTEXT message.
//
// Korean IME should be fixed in the future.
//
break;
} else {
return DefWindowProcWorker(pwnd, message, wParam, lParam, ped->fAnsi);
}
// simple composition character support for FE IME.
case WM_IME_COMPOSITION:
return ECImeComposition(ped, wParam, lParam);
case WM_KILLFOCUS:
//
// when focus is removed from the window,
// composition character should be finalized
//
if (ped && fpImmIsIME(THREAD_HKL())) {
HIMC hImc = fpImmGetContext(hwnd);
if (hImc != NULL_HIMC) {
if (ped->fReplaceCompChr || (ped->wImeStatus & EIMES_COMPLETECOMPSTRKILLFOCUS)) {
// If the composition string to be determined upon kill focus,
// do it now.
fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
} else if (ped->fInReconversion) {
// If the composition string it not to be determined,
// and if we're in reconversion mode, cancel reconversion now.
fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
}
// Get out from reconversion mode
if (ped->fInReconversion) {
ECInOutReconversionMode(ped, FALSE);
}
fpImmReleaseContext(hwnd, hImc);
}
}
goto HandleEditMsg;
break;
case WM_SETFOCUS:
if (ped && !ped->fFocus) {
HKL hkl = THREAD_HKL();
if (fpImmIsIME(hkl)) {
HIMC hImc;
hImc = fpImmGetContext(hwnd);
if (hImc) {
LPINPUTCONTEXT lpImc;
if (ped->wImeStatus & EIMES_CANCELCOMPSTRINFOCUS) {
// cancel when in-focus
fpImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
}
ECImmSetCompositionFont(ped);
if ((lpImc = fpImmLockIMC(hImc)) != NULL) {
// We presume the CompForm will reset to CFS_DEFAULT,
// when the edit control loses Focus.
// IMEWndProc32 will call ImmSetCompositionWindow with
// CFS_DEFAULT, when it receive WM_IME_SETCONTEXT.
lpImc->fdw31Compat |= F31COMPAT_ECSETCFS;
fpImmUnlockIMC(hImc);
}
fpImmReleaseContext(hwnd, hImc);
}
//
// force to set IME composition window when
// first getting focus.
//
ped->ptScreenBounding.x = -1;
ped->ptScreenBounding.y = -1;
}
/*
* Insert and replace flags are initialized when the edit control gets the focus.
*
* Compat hack: a bogus application tries to cheat the system by sending input messages
* *before* it sets the focus to the edit control. They rely on the flags not being set
* at WM_SETFOCUS. Raid #411686
*/
if ((GetAppCompatFlags2(VER40) & GACF2_NO_INIT_ECFLAGS_ON_SETFOCUS) == 0) {
ECInitInsert(ped, hkl);
}
}
goto HandleEditMsg;
break;
case WM_IME_REQUEST:
// simple ImeRequest Handler
return EcImeRequestHandler(ped, wParam, lParam);
case WM_CREATE:
if (ped)
ECEnableDisableIME(ped);
goto HandleEditMsg;
break;
default:
HandleEditMsg:
/* (picked up from NT40FE SP3)
* HACK ALERT: We may receive messages before the PED has been
* allocated (eg: WM_GETMINMAXINFO is sent before WM_NCCREATE)
* so we must test ped before dreferencing.
*/
if (ped != NULL) {
if (ped->fSingle) {
lreturn = SLEditWndProc(hwnd, ped, message, wParam, lParam);
} else {
lreturn = MLEditWndProc(hwnd, ped, message, wParam, lParam);
}
}
}
return lreturn;
}
/***************************************************************************\
* ECFindXORblks
*
* This finds the XOR of lpOldBlk and lpNewBlk and return s resulting blocks
* through the lpBlk1 and lpBlk2; This could result in a single block or
* at the maximum two blocks;
* If a resulting block is empty, then it's StPos field has -1.
* NOTE:
* When called from MultiLine edit control, StPos and EndPos fields of
* these blocks have the Starting line and Ending line of the block;
* When called from SingleLine edit control, StPos and EndPos fields
* of these blocks have the character index of starting position and
* ending position of the block.
*
* History:
\***************************************************************************/
void ECFindXORblks(
LPBLOCK lpOldBlk,
LPBLOCK lpNewBlk,
LPBLOCK lpBlk1,
LPBLOCK lpBlk2)
{
if (lpOldBlk->StPos >= lpNewBlk->StPos) {
lpBlk1->StPos = lpNewBlk->StPos;
lpBlk1->EndPos = min(lpOldBlk->StPos, lpNewBlk->EndPos);
} else {
lpBlk1->StPos = lpOldBlk->StPos;
lpBlk1->EndPos = min(lpNewBlk->StPos, lpOldBlk->EndPos);
}
if (lpOldBlk->EndPos <= lpNewBlk->EndPos) {
lpBlk2->StPos = max(lpOldBlk->EndPos, lpNewBlk->StPos);
lpBlk2->EndPos = lpNewBlk->EndPos;
} else {
lpBlk2->StPos = max(lpNewBlk->EndPos, lpOldBlk->StPos);
lpBlk2->EndPos = lpOldBlk->EndPos;
}
}
/***************************************************************************\
* ECCalcChangeSelection
*
* This function finds the XOR between two selection blocks(OldBlk and NewBlk)
* and return s the resulting areas thro the same parameters; If the XOR of
* both the blocks is empty, then this return s FALSE; Otherwise TRUE.
*
* NOTE:
* When called from MultiLine edit control, StPos and EndPos fields of
* these blocks have the Starting line and Ending line of the block;
* When called from SingleLine edit control, StPos and EndPos fields
* of these blocks have the character index of starting position and
* ending position of the block.
*
* History:
\***************************************************************************/
BOOL ECCalcChangeSelection(
PED ped,
ICH ichOldMinSel,
ICH ichOldMaxSel,
LPBLOCK OldBlk,
LPBLOCK NewBlk)
{
BLOCK Blk[2];
int iBlkCount = 0;
Blk[0].StPos = Blk[0].EndPos = Blk[1].StPos = Blk[1].EndPos = 0xFFFFFFFF;
/*
* Check if the Old selection block existed
*/
if (ichOldMinSel != ichOldMaxSel) {
/*
* Yes! Old block existed.
*/
Blk[0].StPos = OldBlk->StPos;
Blk[0].EndPos = OldBlk->EndPos;
iBlkCount++;
}
/*
* Check if the new Selection block exists
*/
if (ped->ichMinSel != ped->ichMaxSel) {
/*
* Yes! New block exists
*/
Blk[1].StPos = NewBlk->StPos;
Blk[1].EndPos = NewBlk->EndPos;
iBlkCount++;
}
/*
* If both the blocks exist find the XOR of them
*/
if (iBlkCount == 2) {
/*
* Check if both blocks start at the same character position
*/
if (ichOldMinSel == ped->ichMinSel) {
/*
* Check if they end at the same character position
*/
if (ichOldMaxSel == ped->ichMaxSel)
return FALSE; /* Nothing changes */
Blk[0].StPos = min(NewBlk -> EndPos, OldBlk -> EndPos);
Blk[0].EndPos = max(NewBlk -> EndPos, OldBlk -> EndPos);
Blk[1].StPos = 0xFFFFFFFF;
} else {
if (ichOldMaxSel == ped->ichMaxSel) {
Blk[0].StPos = min(NewBlk->StPos, OldBlk->StPos);
Blk[0].EndPos = max(NewBlk->StPos, OldBlk->StPos);
Blk[1].StPos = 0xFFFFFFFF;
} else {
ECFindXORblks(OldBlk, NewBlk, &Blk[0], &Blk[1]);
}
}
}
RtlCopyMemory(OldBlk, &Blk[0], sizeof(BLOCK));
RtlCopyMemory(NewBlk, &Blk[1], sizeof(BLOCK));
return TRUE; /* Yup , There is something to paint */
}
/***************************************************************************\
* ECGetControlBrush
*
* Client side optimization replacement for NtUserGetControlBrush
*
* message is one of the WM_CTLCOLOR* messages.
*
\***************************************************************************/
HBRUSH ECGetControlBrush(
PED ped,
HDC hdc,
LONG message)
{
PWND pwndSend;
PWND pwndEdit;
pwndEdit = ValidateHwnd(ped->hwnd);
if (pwndEdit == (PWND)NULL)
return (HBRUSH)0;
if ((pwndSend = (TestwndPopup(pwndEdit) ? pwndEdit->spwndOwner : pwndEdit->spwndParent)) == NULL)
pwndSend = pwndEdit;
else
pwndSend = REBASEPTR(pwndEdit, pwndSend);
UserAssert(pwndSend);
if (PtiCurrent() != GETPTI(pwndSend)) {
return (HBRUSH)DefWindowProcWorker(pwndSend, message,
(WPARAM)hdc, (LPARAM)pwndEdit, ped->fAnsi);
}
/*
* By using the correct A/W call we avoid a c/s transition
* on this SendMessage().
*/
return (HBRUSH)SendMessageWorker(pwndSend, message, (WPARAM)hdc,
(LPARAM)ped->hwnd, ped->fAnsi);
}
UINT WINAPI QueryFontAssocStatus(void);
UINT fFontAssocStatus = 0xffff;
/***************************************************************************\
* ECGetDBCSVector( PED ped, BYTE CharSet )
*
* This function sets DBCS Vector for specified character set and sets
* ped->fDBCS flag if needed.
*
* History: 18-Jun-1996 Hideyuki Nagase
\***************************************************************************/
int ECGetDBCSVector(PED ped, HDC hdc, BYTE CharSet)
{
BOOL bDBCSCodePage = FALSE;
/*
* if DEFAUT_CHARSET was passed, we will convert that to Shell charset..
*/
if (CharSet == DEFAULT_CHARSET) {
CharSet = (BYTE)GetTextCharset(hdc);
/*
* if CharSet is still DEFAULT_CHARSET, it means gdi has some problem..
* then just return default.. we get charset from CP_ACP..
*/
if (CharSet == DEFAULT_CHARSET) {
CharSet = (BYTE)GetACPCharSet();
}
}
switch (CharSet) {
case SHIFTJIS_CHARSET:
case HANGEUL_CHARSET:
case CHINESEBIG5_CHARSET:
case GB2312_CHARSET:
bDBCSCodePage = TRUE;
break;
case ANSI_CHARSET: // 0
case SYMBOL_CHARSET: // 2
case OEM_CHARSET: // 255
if (fFontAssocStatus == 0xffff)
fFontAssocStatus = QueryFontAssocStatus();
if ((((CharSet + 2) & 0xf) & fFontAssocStatus)) {
bDBCSCodePage = TRUE;
/*
* Bug 117558, etc.
* Try to get a meaningful character set for associated font.
*/
CharSet = (BYTE)GetACPCharSet();
} else {
bDBCSCodePage = FALSE;
}
break;
default:
bDBCSCodePage = FALSE;
}
if (bDBCSCodePage) {
CHARSETINFO CharsetInfo;
DWORD CodePage;
CPINFO CPInfo;
int lbIX;
if (TranslateCharsetInfo((DWORD *)CharSet, &CharsetInfo, TCI_SRCCHARSET)) {
CodePage = CharsetInfo.ciACP;
} else {
CodePage = CP_ACP;
}
GetCPInfo(CodePage, &CPInfo);
for (lbIX=0 ; CPInfo.LeadByte[lbIX] != 0 ; lbIX+=2) {
ped->DBCSVector[lbIX ] = CPInfo.LeadByte[lbIX];
ped->DBCSVector[lbIX+1] = CPInfo.LeadByte[lbIX+1];
}
ped->DBCSVector[lbIX ] = 0x0;
ped->DBCSVector[lbIX+1] = 0x0;
} else {
ped->DBCSVector[0] = 0x0;
ped->DBCSVector[1] = 0x0;
}
//
// Final check: if the font supports DBCS glyphs
//
// If we've got a font with DBCS glyphs, let's mark PED so.
// But since the font's primary charset is the one other than FE,
// we can only support UNICODE Edit control.
//
// a) GDI performs A/W conversion for ANSI apps based on the primary
// character set in hDC, so it will break anyway.
// b) ANSI applications are only supported on their native system locales:
// GetACPCharSet() is expected to return a FE code page.
// c) ANSI Edit control requires DBCSVector, which cannot be
// initialized without a FE code page.
//
if (!ped->fAnsi) {
FONTSIGNATURE fontSig;
GetTextCharsetInfo(hdc, &fontSig, 0);
if (fontSig.fsCsb[0] & FAREAST_CHARSET_BITS) {
bDBCSCodePage = TRUE;
// Since this is UNICODE, we're not
}
}
return bDBCSCodePage;
}
/***************************************************************************\
* LPSTR ECAnsiNext( ped, lpCurrent )
*
* This function advances string pointer for Edit Control use only.
*
* History:
\***************************************************************************/
LPSTR ECAnsiNext(PED ped, LPSTR lpCurrent)
{
return lpCurrent+((ECIsDBCSLeadByte(ped,*lpCurrent)==TRUE) ? 2 : 1);
}
/***************************************************************************\
* LPSTR ECAnsiPrev( ped, lpBase, lpStr )
*
* This function decrements string pointer for Edit Control use only.
*
* History:
\***************************************************************************/
LPSTR ECAnsiPrev(PED ped, LPSTR lpBase, LPSTR lpStr )
{
LPSTR lpCurrent = lpStr -1;
if (!ped->fDBCS)
return lpCurrent; // just return ( lpStr - 1 )
if (lpBase >= lpCurrent)
return lpBase;
if (ECIsDBCSLeadByte(ped, *lpCurrent)) // this check makes things faster
return (lpCurrent - 1); // 92/04/04 takaok
do {
lpCurrent--;
if (!ECIsDBCSLeadByte(ped, *lpCurrent)) {
lpCurrent++;
break;
}
} while(lpCurrent != lpBase);
return lpStr - (((lpStr - lpCurrent) & 1) ? 1 : 2);
}
/***************************************************************************\
* ICH ECNextIch( ped, pText, ichCurrent )
*
* This function advances string pointer for Edit Control use only.
*
* History:
\***************************************************************************/
ICH ECNextIch( PED ped, LPSTR pStart, ICH ichCurrent )
{
if (!ped->fDBCS || !ped->fAnsi) {
return (ichCurrent + 1);
} else {
ICH ichRet;
LPSTR pText;
if (pStart)
pText = pStart + ichCurrent;
else
pText = (LPSTR)ECLock(ped) + ichCurrent;
ichRet = ichCurrent + ( ECIsDBCSLeadByte(ped, *pText) ? 2 : 1 );
if (!pStart)
ECUnlock(ped);
return (ichRet);
}
}
/***************************************************************************\
* ICH ECPrevIch( ped, LPSTR pStart, ICH ichCurrent )
*
* This function decrements string pointer for Edit Control use only.
*
* History:
\***************************************************************************/
ICH ECPrevIch( PED ped, LPSTR pStart, ICH ichCurrent )
{
LPSTR lpCurrent;
LPSTR lpStr;
LPSTR lpBase;
#ifdef SURROGATE
// Handle Unicode surrogates pairs when CSLPK is loaded
if (ped->fAnsi || !ped->pLpkEditCallout) // if no surrogate processing required
#endif
if (!ped->fDBCS || !ped->fAnsi)
if ( ichCurrent )
return (ichCurrent - 1);
else
return (ichCurrent);
if (ichCurrent <= 1)
return 0;
if (pStart)
lpBase = pStart;
else
lpBase = ECLock(ped);
#ifdef SURROGATE
// Handle characters represented by multiple codepoints
if (ped->fAnsi) {
// ANSI PrevIch with DBCS support
#endif
lpStr = lpBase + ichCurrent;
lpCurrent = lpStr - 1;
if (ECIsDBCSLeadByte(ped,*lpCurrent)) {
if (!pStart)
ECUnlock(ped);
return (ichCurrent - 2);
}
do {
lpCurrent--;
if (!ECIsDBCSLeadByte(ped, *lpCurrent)) {
lpCurrent++;
break;
}
} while(lpCurrent != lpBase);
if (!pStart)
ECUnlock(ped);
return (ichCurrent - (((lpStr - lpCurrent) & 1) ? 1 : 2));
#ifdef SURROGATE
} else {
// Unicode PrevIch with surrogate pair support
ichCurrent--;
if ( (((WCHAR*)lpBase)[ichCurrent] & 0xFC00) == 0xDC00
&& (((WCHAR*)lpBase)[ichCurrent-1] & 0xFC00) == 0xD800) {
ichCurrent--;
}
if (!pStart)
ECUnlock(ped);
return ichCurrent;
}
#endif
}
/***************************************************************************\
* BOOL ECIsDBCSLeadByte( PED ped, BYTE cch )
*
* IsDBCSLeadByte for Edit Control use only.
*
* History: 18-Jun-1996 Hideyuki Nagase
\***************************************************************************/
BOOL ECIsDBCSLeadByte(PED ped, BYTE cch)
{
int i;
if (!ped->fDBCS || !ped->fAnsi)
return (FALSE);
for (i = 0; ped->DBCSVector[i]; i += 2) {
if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch))
return (TRUE);
}
return (FALSE);
}
/***************************************************************************\
* int DBCSCombine(HWND hwnd, int ch)
*
* Assemble two WM_CHAR messages to single DBCS character.
* If program detects first byte of DBCS character in WM_CHAR message,
* it calls this function to obtain second WM_CHAR message from queue.
* finally this routine assembles first byte and second byte into single
* DBCS character.
*
* History:
\***************************************************************************/
WORD DbcsCombine(HWND hwnd, WORD ch)
{
MSG msg;
int i = 10; /* loop counter to avoid the infinite loop */
while (!PeekMessageA(&msg, hwnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
if (--i == 0)
return 0;
Sleep(1);
}
return (WORD)ch | ((WORD)(msg.wParam) << 8);
}
/***************************************************************************\
* ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch )
*
* This function adjusts a current pointer correctly. If a current
* pointer is lying between DBCS first byte and second byte, this
* function adjusts a current pointer to a first byte of DBCS position
* by decrement once.
*
* History:
\***************************************************************************/
ICH ECAdjustIch( PED ped, LPSTR lpstr, ICH ch )
{
ICH newch = ch;
if (!ped->fAnsi || !ped->fDBCS || newch == 0)
return ( ch );
if (!ECIsDBCSLeadByte(ped,lpstr[--newch]))
return ( ch ); // previous char is SBCS
while(1) {
if (!ECIsDBCSLeadByte(ped,lpstr[newch])) {
newch++;
break;
}
if (newch)
newch--;
else
break;
}
return ((ch - newch) & 1) ? ch-1 : ch;
}
/***************************************************************************\
* ICH ECAdjustIchNext( PED ped, LPSTR lpstr, ICH ch )
*
* History:
* 19.Jun.1996 Hideyuki Nagase [hideyukn] - Port from Win95-FarEast version
\***************************************************************************/
ICH FAR PASCAL ECAdjustIchNext(PED ped, LPSTR lpstr, ICH ch)
{
ICH ichNew = ECAdjustIch(ped,lpstr,ch);
LPSTR lpnew = lpstr+ichNew;
// if ch > ichNew then ECAdjustIch adjusted ich.
if (ch > ichNew)
lpnew = ECAnsiNext(ped, lpnew);
return (ICH)(lpnew-lpstr);
}
/***************************************************************************\
* ECUpdateFormat
*
* Computes ped->format and ped->fRtoLReading from dwStyle and dwExStyle.
* Refreshes the display if either are changed.
*
* History:
* May 12, 1997 [samera] wrote it
* May 12, 1997 [dbrown] rewrote it
\***************************************************************************/
void ECUpdateFormat(
PED ped,
DWORD dwStyle,
DWORD dwExStyle)
{
UINT fNewRtoLReading;
UINT uiNewFormat;
// Extract new format and reading order from style
fNewRtoLReading = dwExStyle & WS_EX_RTLREADING ? 1 : 0;
uiNewFormat = dwStyle & ES_FMTMASK;
// WS_EX_RIGHT is ignored unless dwStyle is ES_LEFT
if (uiNewFormat == ES_LEFT && dwExStyle & WS_EX_RIGHT) {
uiNewFormat = ES_RIGHT;
}
// Internally ES_LEFT and ES_RIGHT are swapped for RtoLReading order
// (Think of them as ES_LEADING and ES_TRAILING)
if (fNewRtoLReading) {
switch (uiNewFormat) {
case ES_LEFT: uiNewFormat = ES_RIGHT; break;
case ES_RIGHT: uiNewFormat = ES_LEFT; break;
}
}
// Format change does not cause redisplay by itself
ped->format = uiNewFormat;
// Refresh display on change of reading order
if (fNewRtoLReading != ped->fRtoLReading) {
ped->fRtoLReading = fNewRtoLReading;
if (ped->fWrap) {
// Redo wordwrap
MLBuildchLines(ped, 0, 0, FALSE, NULL, NULL);
MLUpdateiCaretLine(ped);
} else {
// Refresh horizontal scrollbar display
MLScroll(ped, FALSE, 0xffffffff, 0, TRUE);
}
ECInvalidateClient(ped, TRUE);
}
}