Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2517 lines
71 KiB

/****************************************************************************\
* editsl.c - Edit controls rewrite. Version II of edit controls.
*
* Single Line Support Routines
*
* Created: 24-Jul-88 davidds
\****************************************************************************/
#include "precomp.h"
#pragma hdrstop
#define SYS_ALTERNATE 0x2000
/***************************************************************************\
* SLSetCaretPosition AorW
*
* If the window has the focus, find where the caret belongs and move
* it there.
*
* History:
\***************************************************************************/
void SLSetCaretPosition(
PED ped,
HDC hdc)
{
int xPosition;
/*
* We will only position the caret if we have the focus since we don't want
* to move the caret while another window could own it.
*/
if (!ped->fFocus)
return;
if (ped->fCaretHidden) {
NtUserSetCaretPos(-20000, -20000);
return;
}
xPosition = SLIchToLeftXPos(ped, hdc, ped->ichCaret);
/*
* Don't let caret go out of bounds of edit control if there is too much
* text.
*/
xPosition = min(xPosition, ped->rcFmt.right -
((ped->cxSysCharWidth > ped->aveCharWidth) ? 1 : 2));
NtUserSetCaretPos(xPosition, ped->rcFmt.top);
}
/***************************************************************************\
* SLIchToLeftXPos AorW
*
* Given a character index, find its (left side) x coordinate within
* the ped->rcFmt rectangle assuming the character ped->ichScreenStart is at
* coordinates (ped->rcFmt.top, ped->rcFmt.left). A negative value is
* return ed if the character ich is to the left of ped->ichScreenStart. WARNING:
* ASSUMES AT MOST 1000 characters will be VISIBLE at one time on the screen.
* There may be 64K total characters in the editcontrol, but we can only
* display 1000 without scrolling. This shouldn't be a problem obviously.
* !NT
* History:
\***************************************************************************/
int SLIchToLeftXPos(
PED ped,
HDC hdc,
ICH ich)
{
int textExtent;
PSTR pText;
SIZE size;
int cchDiff;
/*
* Check if we are adding lots and lots of chars. A paste for example could
* cause this and GetTextExtents could overflow on this.
*/
cchDiff = (int)ich - (int)ped->ichScreenStart;
if (cchDiff > 1000)
return (30000);
else if (cchDiff < -1000)
return (-30000);
#ifdef SUPPORT_LPK
if (ped->lpfnCharset)
return (int)(* ped->lpfnCharset)(ped, EDITINTL_SLICHTOX, hdc, ich);
#endif
if (ped->fNonPropFont)
return (ped->rcFmt.left + cchDiff*ped->aveCharWidth);
/*
* Check if password hidden chars are being used.
*/
if (ped->charPasswordChar)
return ( ped->rcFmt.left + cchDiff*ped->cPasswordCharWidth);
pText = ECLock(ped);
if (ped->fAnsi) {
if (cchDiff >= 0) {
GetTextExtentPointA(hdc, (LPSTR)(pText + ped->ichScreenStart),
cchDiff, &size);
textExtent = size.cx;
/*
* In case of signed/unsigned overflow since the text extent may be
* greater than maxint. This happens with long single line edit
* controls. The rect we edit text in will never be greater than 30000
* pixels so we are ok if we just ignore them.
*/
if (textExtent < 0 || textExtent > 31000)
textExtent = 30000;
} else {
GetTextExtentPointA(hdc,(LPSTR)(pText + ich), -cchDiff, &size);
textExtent = (-1) * size.cx;
}
} else { //!fAnsi
if (cchDiff >= 0) {
GetTextExtentPointW(hdc, (LPWSTR)(pText + ped->ichScreenStart*sizeof(WCHAR)),
cchDiff, &size);
textExtent = size.cx;
/*
* In case of signed/unsigned overflow since the text extent may be
* greater than maxint. This happens with long single line edit
* controls. The rect we edit text in will never be greater than 30000
* pixels so we are ok if we just ignore them.
*/
if (textExtent < 0 || textExtent > 31000)
textExtent = 30000;
} else {
GetTextExtentPointW(hdc,(LPWSTR)(pText + ich*sizeof(WCHAR)), -cchDiff, &size);
textExtent = (-1) * size.cx;
}
}
ECUnlock(ped);
return (ped->rcFmt.left + textExtent -
(textExtent ? ped->charOverhang : 0));
}
/***************************************************************************\
* SLSetSelection AorW
*
* Sets the PED to have the new selection specified.
*
* History:
\***************************************************************************/
void SLSetSelection(
PED ped,
ICH ichSelStart,
ICH ichSelEnd)
{
HDC hdc = ECGetEditDC(ped, FALSE );
if (ichSelStart == 0xFFFFFFFF) {
/*
* Set no selection if we specify -1
*/
ichSelStart = ichSelEnd = ped->ichCaret;
}
/*
* Bounds ichSelStart, ichSelEnd are checked in SLChangeSelection...
*/
SLChangeSelection(ped, hdc, ichSelStart, ichSelEnd);
/*
* Put the caret at the end of the selected text
*/
ped->ichCaret = ped->ichMaxSel;
SLSetCaretPosition(ped, hdc);
/*
* We may need to scroll the text to bring the caret into view...
*/
SLScrollText(ped, hdc);
ECReleaseEditDC(ped, hdc, FALSE);
}
/***************************************************************************\
*
* SLGetClipRect()
*
\***************************************************************************/
void SLGetClipRect(
PED ped,
HDC hdc,
ICH ichStart,
int iCount,
LPRECT lpClipRect )
{
int iStCount;
PSTR pText;
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
(* ped->lpfnCharset)(ped, EDITINTL_GETCLIPRECT, hdc, ichStart,
iCount, lpClipRect);
return;
}
#endif
CopyRect(lpClipRect, &ped->rcFmt);
pText = ECLock(ped) ;
// Calculates the starting pos for this piece of text
if ((iStCount = (int)(ichStart - ped->ichScreenStart)) > 0) {
if (ped->charPasswordChar)
lpClipRect->left += ped->cPasswordCharWidth * iStCount;
else {
SIZE size ;
if ( ped->fAnsi )
GetTextExtentPointA(hdc, pText + ped->ichScreenStart,
iStCount, &size);
else
GetTextExtentPointW(hdc, ((LPWSTR)pText) + ped->ichScreenStart,
iStCount, &size);
lpClipRect->left += size.cx - ped->charOverhang;
}
} else {
// Reset the values to visible portions
iCount -= (ped->ichScreenStart - ichStart);
ichStart = ped->ichScreenStart;
}
if (iCount < 0) {
/*
* This is not in the visible area of the edit control, so return
* an empty rect.
*/
SetRectEmpty(lpClipRect);
ECUnlock(ped);
return;
}
if (ped->charPasswordChar)
lpClipRect->right = lpClipRect->left + ped->cPasswordCharWidth * iCount;
else {
SIZE size ;
if ( ped->fAnsi) {
GetTextExtentPointA(hdc, pText + ichStart, iCount, &size);
} else {
GetTextExtentPointW(hdc, ((LPWSTR)pText) + ichStart, iCount, &size);
}
lpClipRect->right = lpClipRect->left + size.cx - ped->charOverhang;
}
ECUnlock(ped);
}
/***************************************************************************\
* SLChangeSelection AorW
*
* Changes the current selection to have the specified starting and
* ending values. Properly highlights the new selection and unhighlights
* anything deselected. If NewMinSel and NewMaxSel are out of order, we swap
* them. Doesn't update the caret position.
*
* History:
\***************************************************************************/
void SLChangeSelection(
PED ped,
HDC hdc,
ICH ichNewMinSel,
ICH ichNewMaxSel)
{
ICH temp;
ICH ichOldMinSel;
ICH ichOldMaxSel;
if (ichNewMinSel > ichNewMaxSel) {
temp = ichNewMinSel;
ichNewMinSel = ichNewMaxSel;
ichNewMaxSel = temp;
}
ichNewMinSel = min(ichNewMinSel, ped->cch);
ichNewMaxSel = min(ichNewMaxSel, ped->cch);
/*
* Preserve the Old selection
*/
ichOldMinSel = ped->ichMinSel;
ichOldMaxSel = ped->ichMaxSel;
/*
* Set new selection
*/
ped->ichMinSel = ichNewMinSel;
ped->ichMaxSel = ichNewMaxSel;
/*
* We will find the intersection of current selection rectangle with the new
* selection rectangle. We will then invert the parts of the two rectangles
* not in the intersection.
*/
if (_IsWindowVisible(ped->pwnd) && (ped->fFocus || ped->fNoHideSel)) {
BLOCK Blk[2];
int i;
RECT rc;
if (ped->fFocus)
NtUserHideCaret(ped->hwnd);
Blk[0].StPos = ichOldMinSel;
Blk[0].EndPos = ichOldMaxSel;
Blk[1].StPos = ped->ichMinSel;
Blk[1].EndPos = ped->ichMaxSel;
if (ECCalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel,
(LPBLOCK)&Blk[0], (LPBLOCK)&Blk[1])) {
//
// Paint the rectangles where selection has changed.
// Paint both Blk[0] and Blk[1], if they exist.
//
for (i = 0; i < 2; i++) {
if (Blk[i].StPos != 0xFFFFFFFF) {
SLGetClipRect(ped, hdc, Blk[i].StPos,
Blk[i].EndPos - Blk[i].StPos, (LPRECT)&rc);
SLDrawLine(ped, hdc, rc.left, rc.right, Blk[i].StPos,
Blk[i].EndPos - Blk[i].StPos,
((Blk[i].StPos >= ped->ichMinSel) &&
(Blk[i].StPos < ped->ichMaxSel)));
}
}
}
//
// Update caret.
//
SLSetCaretPosition(ped, hdc);
if (ped->fFocus)
NtUserShowCaret(ped->hwnd);
}
}
/***************************************************************************\
*
* SLDrawLine()
*
* This draws the line starting from ichStart, iCount number of characters;
* fSelStatus is TRUE if we're to draw the text as selected.
*
\***************************************************************************/
void SLDrawLine(
PED ped,
HDC hdc,
int xClipStPos,
int xClipEndPos,
ICH ichStart,
int iCount,
BOOL fSelStatus )
{
RECT rc;
RECT rcClip;
PSTR pText;
DWORD rgbSaveBk;
DWORD rgbSaveText;
DWORD wSaveBkMode;
int iStCount;
ICH ichNewStart;
HBRUSH hbrBack;
//
// Anything to draw?
//
if (xClipStPos >= xClipEndPos || !_IsWindowVisible(ped->pwnd) )
return;
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
(* ped->lpfnCharset)(ped, EDITINTL_SLDRAWLINE, hdc, xClipStPos,
xClipEndPos, ichStart, iCount, fSelStatus);
return ;
}
#endif
// Reset ichStart to take care of the negative C widths
ichNewStart = max((int)(ichStart - ped->wMaxNegCcharPos), 0);
// Reset ichCount to take care of the negative C and A widths
iCount = (int)(min(ichStart+iCount+ped->wMaxNegAcharPos, ped->cch)
- ichNewStart);
ichStart = ichNewStart;
//
// Reset ichStart and iCount to the first one visible on the screen
//
if (ichStart < ped->ichScreenStart) {
if (ichStart+iCount < ped->ichScreenStart)
return;
iCount -= (ped->ichScreenStart-ichStart);
ichStart = ped->ichScreenStart;
}
CopyRect(&rc, &ped->rcFmt);
//
// Set the drawing rectangle
//
rcClip.left = xClipStPos;
rcClip.right = xClipEndPos;
rcClip.top = rc.top;
rcClip.bottom = rc.bottom;
//
// Set the proper clipping rectangle
//
ECSetEditClip(ped, hdc, TRUE);
pText = ECLock(ped);
//
// Calculate the starting pos for this piece of text
//
if (iStCount = (int)(ichStart - ped->ichScreenStart)) {
if (ped->charPasswordChar)
rc.left += ped->cPasswordCharWidth * iStCount;
else {
SIZE size;
if ( ped->fAnsi )
GetTextExtentPointA(hdc, pText + ped->ichScreenStart,
iStCount, &size);
else
GetTextExtentPointW(hdc, ((LPWSTR)pText) + ped->ichScreenStart,
iStCount, &size);
rc.left += size.cx - ped->charOverhang;
}
}
//
// Set the background mode before calling NtUserGetControlBrush so that the app
// can change it to TRANSPARENT if it wants to.
//
SetBkMode(hdc, OPAQUE);
if (fSelStatus) {
hbrBack = SYSHBR(HIGHLIGHT);
if (hbrBack == NULL) {
goto sldl_errorexit;
}
rgbSaveBk = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
rgbSaveText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
//
// We always want to send this so that the app has a chance to muck
// with the DC.
//
// Note that ReadOnly and Disabled edit fields are drawn as "static"
// instead of as "active."
//
hbrBack = ECGetBrush(ped, hdc);
rgbSaveText = GetTextColor(hdc);
}
//
// Erase the rectangular area before text is drawn. Note that we inflate
// the rect by 1 so that the selection color has a one pixel border around
// the text.
//
InflateRect(&rcClip, 0, 1);
FillRect(hdc, &rcClip, hbrBack);
InflateRect(&rcClip, 0, -1);
if (ped->charPasswordChar) {
wSaveBkMode = SetBkMode(hdc, TRANSPARENT);
for (iStCount = 0; iStCount < iCount; iStCount++) {
if ( ped->fAnsi )
ExtTextOutA(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip,
(LPSTR)&ped->charPasswordChar, 1, NULL);
else
ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip,
(LPWSTR)&ped->charPasswordChar, 1, NULL);
rc.left += ped->cPasswordCharWidth;
}
SetBkMode(hdc, wSaveBkMode);
} else {
if ( ped->fAnsi )
ExtTextOutA(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip,
pText+ichStart,iCount, NULL);
else
ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip,
((LPWSTR)pText)+ichStart,iCount, NULL);
}
SetTextColor(hdc, rgbSaveText);
if (fSelStatus) {
SetBkColor(hdc, rgbSaveBk);
}
sldl_errorexit:
ECUnlock(ped);
}
/***************************************************************************\
* SLGetBlkEnd AorW
*
* Given a Starting point and and end point, this function return s whether the
* first few characters fall inside or outside the selection block and if so,
* howmany characters?
*
* History:
\***************************************************************************/
int SLGetBlkEnd(
PED ped,
ICH ichStart,
ICH ichEnd,
BOOL FAR *lpfStatus)
{
*lpfStatus = FALSE;
if (ichStart >= ped->ichMinSel) {
if (ichStart >= ped->ichMaxSel)
return (ichEnd - ichStart);
*lpfStatus = TRUE;
return (min(ichEnd, ped->ichMaxSel) - ichStart);
}
return (min(ichEnd, ped->ichMinSel) - ichStart);
}
/***************************************************************************\
* SLDrawText AorW
*
* Draws text for a single line edit control in the rectangle
* specified by ped->rcFmt. If ichStart == 0, starts drawing text at the left
* side of the window starting at character index ped->ichScreenStart and draws
* as much as will fit. If ichStart > 0, then it appends the characters
* starting at ichStart to the end of the text showing in the window. (ie. We
* are just growing the text length and keeping the left side
* (ped->ichScreenStart to ichStart characters) the same. Assumes the hdc came
* from ECGetEditDC so that the caret and such are properly hidden.
*
* History:
\***************************************************************************/
void SLDrawText(
PED ped,
HDC hdc,
ICH ichStart)
{
ICH cchToDraw;
RECT rc;
PSTR pText;
BOOL fSelStatus;
int iCount, iStCount;
ICH ichEnd;
BOOL fNoSelection;
BOOL fCalcRect;
BOOL fDrawLeftMargin = FALSE;
BOOL fDrawEndOfLineStrip = FALSE;
SIZE size;
if (!_IsWindowVisible(ped->pwnd))
return;
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
(* ped->lpfnCharset)(ped, EDITINTL_SLDRAWTEXT, hdc, ichStart);
SLSetCaretPosition(ped, hdc);
return ;
}
#endif
if (ichStart < ped->ichScreenStart )
ichStart = ped->ichScreenStart;
CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt);
/*
* Find out how many characters will fit on the screen so that we don't do
* any needless drawing.
*/
pText = ECLock(ped);
cchToDraw = ECCchInWidth(ped, hdc,
(LPSTR)(pText + ped->ichScreenStart * ped->cbChar),
ped->cch - ped->ichScreenStart, rc.right - rc.left, TRUE);
ichEnd = ped->ichScreenStart + cchToDraw;
/*
* There is no selection if,
* 1. MinSel and MaxSel are equal OR
* 2. (This has lost the focus AND Selection is to be hidden)
*/
fNoSelection = ((ped->ichMinSel == ped->ichMaxSel) || (!ped->fFocus && !ped->fNoHideSel));
if (iStCount = (int)(ichStart - ped->ichScreenStart)) {
if (ped->charPasswordChar)
rc.left += ped->cPasswordCharWidth * iStCount;
else {
if ( ped->fAnsi )
GetTextExtentPointA(hdc, pText + ped->ichScreenStart,
iStCount, &size);
else
GetTextExtentPointW(hdc, ((LPWSTR)pText) + ped->ichScreenStart,
iStCount, &size);
/*
* For Non TrueType fonts, ped->wMaxNegA is equal to ped->charOverhang
*/
rc.left += size.cx - ped->wMaxNegA;
}
}
//
// If this is the begining of the whole line, we may have to draw a blank
// strip at the begining.
//
if ((ichStart == 0) && ped->wLeftMargin)
fDrawLeftMargin = TRUE;
//
// If there is nothing to draw, that means we need to draw the end of
// line strip, which erases the last character.
//
if (ichStart == ichEnd) {
fDrawEndOfLineStrip = TRUE;
rc.left -= ped->wLeftMargin;
}
while (ichStart < ichEnd) {
fCalcRect = TRUE;
if (fNoSelection) {
fSelStatus = FALSE;
iCount = ichEnd - ichStart;
} else {
if (fDrawLeftMargin) {
iCount = 0;
fSelStatus = FALSE;
fCalcRect = FALSE;
rc.right = rc.left;
} else
iCount = SLGetBlkEnd(ped, ichStart, ichEnd,
(BOOL *)&fSelStatus);
}
if (ichStart+iCount == ichEnd) {
if (fSelStatus)
fDrawEndOfLineStrip = TRUE;
else {
rc.right = ped->rcFmt.right + ped->wRightMargin;
fCalcRect = FALSE;
}
}
if (fCalcRect) {
if (ped->charPasswordChar)
rc.right = rc.left + ped->cPasswordCharWidth * iCount;
else {
if ( ped->fAnsi )
GetTextExtentPointA(hdc, pText + ichStart,
iCount, &size);
else
GetTextExtentPointW(hdc, ((LPWSTR)pText) + ichStart,
iCount, &size);
rc.right = rc.left + size.cx;
/*
* The extent is equal to the advance width. So for TrueType fonts
* we need to take care of Neg A and C. For non TrueType, the extent
* includes the overhang.
* If drawing the selection, draw only the advance width
*/
if (fSelStatus) {
rc.right -= ped->charOverhang;
} else if (ped->fTrueType) {
rc.right += ped->wMaxNegC;
if (iStCount > 0) {
rc.right += ped->wMaxNegA;
iStCount = 0;
}
}
} /* if (ped->charPasswordChar) */
}
if (fDrawLeftMargin) {
fDrawLeftMargin = FALSE;
rc.left -= ped->wLeftMargin;
if (rc.right < rc.left) {
rc.right = rc.left;
}
}
SLDrawLine(ped, hdc, rc.left, rc.right, ichStart, iCount, fSelStatus);
ichStart += iCount;
rc.left = rc.right;
/*
* If we're going to draw the selection, adjust rc.left
* to include advance width of the selected text
* For non TT fonts, ped->wMaxNegC equals ped->charOverhang
*/
if (!fSelStatus && (iCount != 0) && (ichStart < ichEnd)) {
rc.left -= ped->wMaxNegC;
}
}
ECUnlock(ped);
// Check if anything to be erased on the right hand side
if (fDrawEndOfLineStrip &&
(rc.left < (rc.right = (ped->rcFmt.right+ped->wRightMargin))))
SLDrawLine(ped, hdc, rc.left, rc.right, ichStart, 0, FALSE);
SLSetCaretPosition(ped, hdc);
}
/***************************************************************************\
* SLScrollText AorW
*
* Scrolls the text to bring the caret into view. If the text is
* scrolled, the current selection is unhighlighted. Returns TRUE if the text
* is scrolled else return s false.
*
* History:
\***************************************************************************/
BOOL SLScrollText(
PED ped,
HDC hdc)
{
PSTR pTextScreenStart;
ICH scrollAmount;
ICH newScreenStartX = ped->ichScreenStart;
ICH cch;
if (!ped->fAutoHScroll)
return (FALSE);
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
newScreenStartX=(ICH)(* ped->lpfnCharset)(ped, EDITINTL_SLSCROLLTEXT, hdc);
} else {
#endif
/*
* Calculate the new starting screen position
*/
if (ped->ichCaret <= ped->ichScreenStart) {
/*
* Caret is to the left of the starting text on the screen we must
* scroll the text backwards to bring it into view. Watch out when
* subtracting unsigned numbers when we have the possibility of going
* negative.
*/
pTextScreenStart = ECLock(ped);
scrollAmount = ECCchInWidth(ped, hdc, (LPSTR)pTextScreenStart,
ped->ichCaret, (ped->rcFmt.right - ped->rcFmt.left) / 4, FALSE);
newScreenStartX = ped->ichCaret - scrollAmount;
ECUnlock(ped);
} else if (ped->ichCaret != ped->ichScreenStart) {
pTextScreenStart = ECLock(ped);
pTextScreenStart += ped->ichScreenStart * ped->cbChar;
cch = ECCchInWidth(ped, hdc, (LPSTR)pTextScreenStart,
ped->ichCaret - ped->ichScreenStart,
ped->rcFmt.right - ped->rcFmt.left, FALSE);
if (cch < ped->ichCaret - ped->ichScreenStart) {
/*
* Scroll Forward 1/4 -- if that leaves some empty space
* at the end, scroll back enough to fill the space
*/
newScreenStartX = ped->ichCaret - (3 * cch / 4);
cch = ECCchInWidth(ped, hdc, (LPSTR)pTextScreenStart,
ped->cch - ped->ichScreenStart,
ped->rcFmt.right - ped->rcFmt.left, FALSE);
if (newScreenStartX > (ped->cch - cch))
newScreenStartX = ped->cch - cch;
}
ECUnlock(ped);
}
#ifdef SUPPORT_LPK
}
#endif
if (ped->ichScreenStart != newScreenStartX) {
// Check if we have to wipe out the left margin
if (ped->wLeftMargin && (ped->ichScreenStart == 0)) {
RECT rc;
HBRUSH hBrush;
hBrush = ECGetBrush(ped, hdc);
CopyInflateRect(&rc, &ped->rcFmt, 0, 1);
rc.right = rc.left;
rc.left -= ped->wLeftMargin;
FillRect(hdc, &rc, hBrush);
}
ped->ichScreenStart = newScreenStartX;
SLDrawText(ped, hdc, 0);
// Caret pos is set by SLDrawText().
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* SLInsertText AorW
*
* Adds up to cchInsert characters from lpText to the ped starting at
* ichCaret. If the ped only allows a maximum number of characters, then we
* will only add that many characters to the ped and send a EN_MAXTEXT
* notification code to the parent of the ec. Also, if !fAutoHScroll, then we
* only allow as many chars as will fit in the client rectangle. The number of
* characters actually added is return ed (could be 0). If we can't allocate
* the required space, we notify the parent with EN_ERRSPACE and no characters
* are added.
*
* History:
\***************************************************************************/
ICH SLInsertText(
PED ped,
LPSTR lpText,
ICH cchInsert)
{
HDC hdc;
PSTR pText;
ICH cchInsertCopy = cchInsert;
ICH cchT;
int textWidth;
SIZE size;
/*
* First determine exactly how many characters from lpText we can insert
* into the ped.
*/
if( ped->cchTextMax <= ped->cch)
cchInsert = 0;
else {
if (!ped->fAutoHScroll) {
pText = ECLock(ped);
hdc = ECGetEditDC(ped, TRUE);
cchInsert = min(cchInsert, (unsigned)(ped->cchTextMax - ped->cch));
if (ped->charPasswordChar)
textWidth = ped->cch * ped->cPasswordCharWidth;
else {
if (ped->fAnsi)
GetTextExtentPointA(hdc, (LPSTR)pText, ped->cch, &size);
else
GetTextExtentPointW(hdc, (LPWSTR)pText, ped->cch, &size);
textWidth = size.cx;
}
cchT = ECCchInWidth(ped, hdc, lpText, cchInsert,
ped->rcFmt.right - ped->rcFmt.left -
textWidth, TRUE);
cchInsert = min(cchInsert, cchT);
ECUnlock(ped);
ECReleaseEditDC(ped, hdc, TRUE);
} else {
cchInsert = min((unsigned)(ped->cchTextMax - ped->cch), cchInsert);
}
}
/*
* Now try actually adding the text to the ped
*/
if (cchInsert && !ECInsertText(ped, lpText, cchInsert)) {
ECNotifyParent(ped, EN_ERRSPACE);
return (0);
}
if (cchInsert)
ped->fDirty = TRUE; /* Set modify flag */
if (cchInsert < cchInsertCopy) {
/*
* Notify parent that we couldn't insert all the text requested
*/
ECNotifyParent(ped, EN_MAXTEXT);
}
/*
* Update selection extents and the caret position. Note that ECInsertText
* updates ped->ichCaret, ped->ichMinSel, and ped->ichMaxSel to all be after
* the inserted text.
*/
return (cchInsert);
}
/***************************************************************************\
* SLPasteText AorW
*
* Pastes a line of text from the clipboard into the edit control
* starting at ped->ichMaxSel. Updates ichMaxSel and ichMinSel to point to
* the end of the inserted text. Notifies the parent if space cannot be
* allocated. Returns how many characters were inserted.
*
* History:
\***************************************************************************/
ICH PASCAL NEAR SLPasteText(
PED ped)
{
HANDLE hData;
LPSTR lpchClip;
ICH cchAdded;
ICH clipLength;
if (!OpenClipboard(ped->hwnd))
return (0);
if (!(hData = GetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT))) {
CloseClipboard();
return (0);
}
USERGLOBALLOCK(hData, lpchClip);
if (ped->fAnsi) {
LPSTR lpchClip2 = lpchClip;
/*
* Find the first carrage return or line feed. Just add text to that point.
*/
clipLength = (UINT)strlen(lpchClip);
for (cchAdded = 0; cchAdded < clipLength; cchAdded++)
if (*lpchClip2++ == 0x0D)
break;
} else { // !fAnsi
LPWSTR lpwstrClip2 = (LPWSTR)lpchClip;
/*
* Find the first carrage return or line feed. Just add text to that point.
*/
clipLength = (UINT)wcslen((LPWSTR)lpchClip);
for (cchAdded = 0; cchAdded < clipLength; cchAdded++)
if (*lpwstrClip2++ == 0x0D)
break;
}
/*
* Insert the text (SLInsertText checks line length)
*/
cchAdded = SLInsertText(ped, lpchClip, cchAdded);
USERGLOBALUNLOCK(hData);
CloseClipboard();
return (cchAdded);
}
/***************************************************************************\
* SLReplaceSel AorW
*
* Replaces the text in the current selection with the given text.
*
* History:
\***************************************************************************/
void SLReplaceSel(
PED ped,
LPSTR lpText)
{
UINT cchText;
//
// Delete text, putting it into the clean undo buffer.
//
ECEmptyUndo(Pundo(ped));
ECDeleteText(ped);
//
// B#3356
// Some apps do "clear" by selecting all of the text, then replacing it
// with "", in which case SLInsertText() will return 0. But that
// doesn't mean failure...
//
if ( ped->fAnsi )
cchText = strlen(lpText);
else
cchText = wcslen((LPWSTR)lpText);
if (cchText) {
BOOL fFailed;
UNDO undo;
HWND hwndSave;
//
// Save undo buffer, but DO NOT CLEAR IT!
//
ECSaveUndo(Pundo(ped), &undo, FALSE);
hwndSave = ped->hwnd;
fFailed = (BOOL) !SLInsertText(ped, lpText, cchText);
if (!IsWindow(hwndSave))
return;
if (fFailed) {
//
// UNDO the previous edit.
//
ECSaveUndo(&undo, Pundo(ped), FALSE);
SLUndo(ped);
return;
}
}
//
// Success. So update the display
//
ECNotifyParent(ped, EN_UPDATE);
if (_IsWindowVisible(ped->pwnd)) {
HDC hdc;
hdc = ECGetEditDC(ped, FALSE);
if (!SLScrollText(ped, hdc))
SLDrawText(ped, hdc, 0);
ECReleaseEditDC(ped, hdc, FALSE);
}
ECNotifyParent(ped, EN_CHANGE);
}
/***************************************************************************\
* SLChar AorW
*
* Handles character input
*
* History:
\***************************************************************************/
void SLChar(
PED ped,
DWORD keyValue)
{
HDC hdc;
WCHAR keyPress;
BOOL updateText = FALSE;
HWND hwndSave = ped->hwnd;
if (ped->fAnsi)
keyPress = LOBYTE(keyValue);
else
keyPress = LOWORD(keyValue);
if (ped->fMouseDown || (ped->fReadOnly && keyPress != 3)) {
/*
* Don't do anything if we are in the middle of a mousedown deal or if
* this is a read only edit control, with exception of allowing
* ctrl-C in order to copy to the clipboard.
*/
return ;
}
switch (keyPress) {
case VK_BACK:
DeleteSelection:
if (ECDeleteText(ped))
updateText = TRUE;
break;
default:
if (keyPress >= TEXT(' '))
{
/*
* If this is in [a-z],[A-Z] and we are an ES_NUMBER
* edit field, bail.
*/
if (ped->f40Compat && TestWF(ped->pwnd, EFNUMBER)) {
if (!ECIsCharNumeric(ped, keyPress)) {
goto IllegalChar;
}
}
goto DeleteSelection;
}
break;
}
switch (keyPress) {
case 3:
/*
* CTRL-C Copy
*/
SendMessage(ped->hwnd, WM_COPY, 0, 0L);
return;
case VK_BACK:
/*
* Delete any selected text or delete character left if no sel
*/
if (!updateText && ped->ichMinSel) {
/*
* There was no selection to delete so we just delete character
left if available
*/
ped->ichMinSel--;
ECDeleteText(ped);
updateText = TRUE;
}
break;
case 22: /* CTRL-V Paste */
SendMessage(ped->hwnd, WM_PASTE, 0, 0L);
return;
case 24: /* CTRL-X Cut */
if (ped->ichMinSel == ped->ichMaxSel)
goto IllegalChar;
SendMessage(ped->hwnd, WM_CUT, 0, 0L);
return;
case 26: /* CTRL-Z Undo */
SendMessage(ped->hwnd, EM_UNDO, 0, 0L);
return;
case VK_RETURN:
case VK_ESCAPE:
//
// If this is an edit control for a combobox and the dropdown list
// is visible, forward it up to the combo.
//
if (ped->listboxHwnd && SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L)) {
SendMessage(ped->hwndParent, WM_KEYDOWN, (WPARAM)keyPress, 0L);
} else
goto IllegalChar;
return;
default:
if (keyPress >= TEXT(' ')) {
if (SLInsertText(ped, (LPSTR)&keyPress, 1))
updateText = TRUE;
else
/*
* Beep. Since we couldn't add the text
*/
NtUserMessageBeep(0);
} else {
/*
* User hit an illegal control key
*/
IllegalChar:
NtUserMessageBeep(0);
}
if (!IsWindow(hwndSave))
return;
break;
}
if (updateText) {
/*
* Dirty flag (ped->fDirty) was set when we inserted text
*/
ECNotifyParent(ped, EN_UPDATE);
hdc = ECGetEditDC(ped, FALSE);
if (!SLScrollText(ped, hdc))
SLDrawText(ped, hdc, max(0, (int)(ped->ichCaret - 1 - ped->wMaxNegCcharPos)));
ECReleaseEditDC(ped, hdc, FALSE);
ECNotifyParent(ped, EN_CHANGE);
}
}
/***************************************************************************\
* SLKeyDown AorW
*
* Handles cursor movement and other VIRT KEY stuff. keyMods allows
* us to make SLKeyDownHandler calls and specify if the modifier keys (shift
* and control) are up or down. This is useful for imnplementing the
* cut/paste/clear messages for single line edit controls. If keyMods == 0,
* we get the keyboard state using GetKeyState(VK_SHIFT) etc. Otherwise, the
* bits in keyMods define the state of the shift and control keys.
*
* History:
\***************************************************************************/
void SLKeyDown(
PED ped,
DWORD virtKeyCode,
int keyMods)
{
HDC hdc;
/*
* Variables we will use for redrawing the updated text
*/
ICH newMaxSel = ped->ichMaxSel;
ICH newMinSel = ped->ichMinSel;
/*
* Flags for drawing the updated text
*/
BOOL updateText = FALSE;
BOOL changeSelection = FALSE; /* new selection is specified by
newMinSel, newMaxSel */
/*
* Comparisons we do often
*/
BOOL MinEqMax = (newMaxSel == newMinSel);
BOOL MinEqCar = (ped->ichCaret == newMinSel);
BOOL MaxEqCar = (ped->ichCaret == newMaxSel);
/*
* State of shift and control keys.
*/
int scState;
/*
* Combo box support
*/
BOOL fIsListVisible;
BOOL fIsExtendedUI;
if (ped->fMouseDown) {
/*
* If we are in the middle of a mouse down handler, then don't do
* anything. ie. ignore keyboard input.
*/
return;
}
scState = ECGetModKeys(keyMods);
switch (virtKeyCode) {
case VK_UP:
if ( ped->listboxHwnd ) {
/*
* Handle Combobox support
*/
fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0);
fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0);
if (!fIsListVisible && fIsExtendedUI) {
/*
* For TandyT
*/
DropExtendedUIListBox:
/*
* Since an extendedui combo box doesn't do anything on f4, we
* turn off the extended ui, send the f4 to drop, and turn it
* back on again.
*/
SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 0, 0);
SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0);
SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 1, 0);
return;
} else
goto SendKeyToListBox;
}
/*
* else fall through
*/
case VK_LEFT:
//
// If the caret isn't at the beginning, we can move left
//
if (ped->ichCaret) {
//
// Get new caret pos.
//
if (scState & CTRLDOWN) {
// Move caret word left
ECWord(ped, ped->ichCaret, TRUE, &ped->ichCaret, NULL);
} else {
// Move caret char left
ped->ichCaret--;
}
//
// Get new selection
//
if (scState & SHFTDOWN) {
if (MaxEqCar && !MinEqMax) {
// Reduce selection
newMaxSel = ped->ichCaret;
UserAssert(newMinSel == ped->ichMinSel);
} else {
// Extend selection
newMinSel = ped->ichCaret;
}
} else {
//
// Clear selection
//
newMaxSel = newMinSel = ped->ichCaret;
}
changeSelection = TRUE;
} else {
//
// If the user tries to move left and we are at the 0th
// character and there is a selection, then cancel the
// selection.
//
if ( (ped->ichMaxSel != ped->ichMinSel) &&
!(scState & SHFTDOWN) ) {
changeSelection = TRUE;
newMaxSel = newMinSel = ped->ichCaret;
}
}
break;
case VK_DOWN:
if (ped->listboxHwnd) {
/*
* Handle Combobox support
*/
fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0);
fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0);
if (!fIsListVisible && fIsExtendedUI) {
/*
* For TandyT
*/
goto DropExtendedUIListBox;
} else
goto SendKeyToListBox;
}
/*
* else fall through
*/
case VK_RIGHT:
//
// If the caret isn't at the end, we can move right.
//
if (ped->ichCaret < ped->cch) {
//
// Get new caret pos.
//
if (scState & CTRLDOWN) {
// Move caret word right
ECWord(ped, ped->ichCaret, FALSE, NULL, &ped->ichCaret);
} else {
// Move caret char right
ped->ichCaret++;
}
//
// Get new selection.
//
if (scState & SHFTDOWN) {
if (MinEqCar && !MinEqMax) {
// Reduce selection
newMinSel = ped->ichCaret;
UserAssert(newMaxSel == ped->ichMaxSel);
} else {
// Extend selection
newMaxSel = ped->ichCaret;
}
} else {
// Clear selection
newMaxSel = newMinSel = ped->ichCaret;
}
changeSelection = TRUE;
} else {
//
// If the user tries to move right and we are at the last
// character and there is a selection, then cancel the
// selection.
//
if ( (ped->ichMaxSel != ped->ichMinSel) &&
!(scState & SHFTDOWN) ) {
newMaxSel = newMinSel = ped->ichCaret;
changeSelection = TRUE;
}
}
break;
case VK_HOME:
//
// Move caret to top.
//
ped->ichCaret = 0;
//
// Update selection.
//
if (scState & SHFTDOWN) {
if (MaxEqCar && !MinEqMax) {
// Reduce selection
newMinSel = ped->ichCaret;
newMaxSel = ped->ichMinSel;
} else {
// Extend selection
newMinSel = ped->ichCaret;
}
} else {
// Clear selection
newMaxSel = newMinSel = ped->ichCaret;
}
changeSelection = TRUE;
break;
case VK_END:
//
// Move caret to end.
//
ped->ichCaret = ped->cch;
//
// Update selection.
//
newMaxSel = ped->ichCaret;
if (scState & SHFTDOWN) {
if (MinEqCar && !MinEqMax) {
// Reduce selection
newMinSel = ped->ichMaxSel;
}
} else {
// Clear selection
newMinSel = ped->ichCaret;
}
changeSelection = TRUE;
break;
case VK_DELETE:
if (ped->fReadOnly)
break;
switch (scState) {
case NONEDOWN:
/*
* Clear selection. If no selection, delete (clear) character
* right.
*/
if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) {
/*
* Move cursor forwards and simulate a backspace.
*/
ped->ichCaret++;
ped->ichMaxSel = ped->ichMinSel = ped->ichCaret;
SLChar(ped, (UINT)VK_BACK);
}
if (ped->ichMinSel != ped->ichMaxSel)
SLChar(ped, (UINT)VK_BACK);
break;
case SHFTDOWN:
//
// Send ourself a WM_CUT message if a selection exists.
// Otherwise, delete the left character.
//
if (ped->ichMinSel == ped->ichMaxSel) {
UserAssert(!ped->fEatNextChar);
SLChar(ped, VK_BACK);
} else
SendMessage(ped->hwnd, WM_CUT, 0, 0L);
break;
case CTRLDOWN:
/*
* Delete to end of line if no selection else delete (clear)
* selection.
*/
if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) {
/*
* Move cursor to end of line and simulate a backspace.
*/
ped->ichMaxSel = ped->ichCaret = ped->cch;
}
if (ped->ichMinSel != ped->ichMaxSel)
SLChar(ped, (UINT)VK_BACK);
break;
}
/*
* No need to update text or selection since BACKSPACE message does it
* for us.
*/
break;
case VK_INSERT:
switch (scState) {
case CTRLDOWN:
/*
* Copy current selection to clipboard
*/
SendMessage(ped->hwnd, WM_COPY, (UINT)NULL, (LONG)NULL);
break;
case SHFTDOWN:
SendMessage(ped->hwnd, WM_PASTE, 0, 0L);
break;
}
break;
case VK_F4:
case VK_PRIOR:
case VK_NEXT:
/*
* Send keys to the listbox if we are a part of a combo box. This
* assumes the listbox ignores keyup messages which is correct right
* now.
*/
SendKeyToListBox:
if (ped->listboxHwnd) {
/*
* Handle Combobox support
*/
SendMessage(ped->listboxHwnd, WM_KEYDOWN, virtKeyCode, 0L);
return;
}
}
if (changeSelection || updateText) {
hdc = ECGetEditDC(ped, FALSE);
/*
* Scroll if needed
*/
SLScrollText(ped, hdc);
if (changeSelection)
SLChangeSelection(ped, hdc, newMinSel, newMaxSel);
if (updateText)
SLDrawText(ped, hdc, 0);
ECReleaseEditDC(ped, hdc, FALSE);
if (updateText)
ECNotifyParent(ped, EN_CHANGE);
}
}
/***************************************************************************\
* SLMouseToIch AorW
*
* Returns the closest cch to where the mouse point is.
*
* History:
\***************************************************************************/
ICH SLMouseToIch(
PED ped,
HDC hdc,
LPPOINT mousePt)
{
PSTR pText;
int width = mousePt->x;
SIZE size;
ICH cch;
ICH cchLo, cchHi;
LPSTR lpText;
#ifdef SUPPORT_LPK
if (ped->lpfnCharset)
return (ICH)(* ped->lpfnCharset)(ped, EDITINTL_MOUSETOICH, hdc, mousePt);
#endif
if (width <= ped->rcFmt.left) {
/*
* Return either the first non visible character or return 0 if at
* beginning of text
*/
if (ped->ichScreenStart)
return (ped->ichScreenStart - 1);
else
return (0);
}
if (width > ped->rcFmt.right) {
pText = ECLock(ped);
/*
* Return last char in text or one plus the last char visible
*/
cch = ECCchInWidth(ped, hdc,
(LPSTR)(pText + ped->ichScreenStart * ped->cbChar),
ped->cch - ped->ichScreenStart, ped->rcFmt.right -
ped->rcFmt.left, TRUE) + ped->ichScreenStart;
ECUnlock(ped);
if (cch >= ped->cch)
return (ped->cch);
else
return (cch + 1);
}
/*
* Check if password hidden chars are being used.
*/
if (ped->charPasswordChar)
return min( (DWORD)( (width - ped->rcFmt.left) / ped->cPasswordCharWidth),
ped->cch);
if (!ped->cch)
return (0);
pText = ECLock(ped);
lpText = pText + ped->ichScreenStart * ped->cbChar;
/*
* Initialize Binary Search Bounds
*/
cchLo = 0;
cchHi = ped->cch + 1 - ped->ichScreenStart;
/*
* Binary search for closest char
*/
while (cchLo < cchHi - 1) {
cch = max((cchHi - cchLo) / 2, 1) + cchLo;
if (ped->fAnsi)
GetTextExtentPointA(hdc, lpText, cch, &size);
else
GetTextExtentPointW(hdc, (LPWSTR)lpText, cch, &size);
size.cx -= (ped->aveCharWidth / 2);
if (size.cx <= (width - ped->rcFmt.left))
cchLo = cch;
else
cchHi = cch;
}
ECUnlock(ped);
return cchLo + ped->ichScreenStart;
}
/***************************************************************************\
* SLMouseMotion AorW
*
* <brief description>
*
* History:
\***************************************************************************/
void SLMouseMotion(
PED ped,
UINT message,
UINT virtKeyDown,
LPPOINT mousePt)
{
DWORD selectionl;
DWORD selectionh;
BOOL changeSelection;
ICH newMaxSel;
ICH newMinSel;
HDC hdc;
ICH mouseIch;
changeSelection = FALSE;
newMinSel = ped->ichMinSel;
newMaxSel = ped->ichMaxSel;
hdc = ECGetEditDC(ped, FALSE);
mouseIch = SLMouseToIch(ped, hdc, mousePt);
switch (message) {
case WM_LBUTTONDBLCLK:
// if shift key is down, extend selection to word we double clicked on
// else clear current selection and select word.
ECWord(ped, mouseIch, (mouseIch) ? TRUE : FALSE, &selectionl, &selectionh);
if (!(virtKeyDown & MK_SHIFT)) {
// If shift key isn't down, move caret to mouse point and clear
// old selection
newMinSel = selectionl;
newMaxSel = ped->ichCaret = selectionh;
} else {
// Shiftkey is down so we want to maintain the current selection
// (if any) and just extend or reduce it
if (ped->ichMinSel == ped->ichCaret) {
newMinSel = ped->ichCaret = selectionl;
ECWord(ped, newMaxSel, TRUE, &selectionl, &selectionh);
} else {
newMaxSel = ped->ichCaret = selectionh;
ECWord(ped, newMinSel, FALSE, &selectionl, &selectionh);
}
/*
* v-ronaar: fix bug 24627 - edit selection is weird.
*/
ped->ichMaxSel = ped->ichCaret;
}
ped->ichStartMinSel = selectionl;
ped->ichStartMaxSel = selectionh;
goto InitDragSelect;
case WM_MOUSEMOVE:
//
// We know the mouse button's down -- otherwise the OPTIMIZE
// test would've failed in SLEditWndProc and never called
//
changeSelection = TRUE;
// Extend selection, move caret word right
if (ped->ichStartMinSel || ped->ichStartMaxSel) {
// We're in WORD SELECT mode
BOOL fReverse = (mouseIch <= ped->ichStartMinSel);
ECWord(ped, mouseIch, !fReverse, &selectionl, &selectionh);
if (fReverse) {
newMinSel = ped->ichCaret = selectionl;
newMaxSel = ped->ichStartMaxSel;
} else {
newMinSel = ped->ichStartMinSel;
newMaxSel = ped->ichCaret = selectionh;
}
} else if ((ped->ichMinSel == ped->ichCaret) &&
(ped->ichMinSel != ped->ichMaxSel))
// Reduce selection extent
newMinSel = ped->ichCaret = mouseIch;
else
// Extend selection extent
newMaxSel = ped->ichCaret=mouseIch;
break;
case WM_LBUTTONDOWN:
// If we currently don't have the focus yet, try to get it.
if (!ped->fFocus) {
if (!ped->fNoHideSel)
// Clear the selection before setting the focus so that we
// don't get refresh problems and flicker. Doesn't matter
// since the mouse down will end up changing it anyway.
ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;
NtUserSetFocus(ped->hwnd);
//
// BOGUS
// (1) We should see if SetFocus() succeeds.
// (2) We should ignore mouse messages if the first window
// ancestor with a caption isn't "active."
//
// If we are part of a combo box, then this is the first time
// the edit control is getting the focus so we just want to
// highlight the selection and we don't really want to position
// the caret.
if (ped->listboxHwnd)
break;
// We yield at SetFocus -- text might have changed at that point
// update selection and caret info accordingly
// FIX for bug # 11743 -- JEFFBOG 8/23/91
newMaxSel = ped->ichMaxSel;
newMinSel = ped->ichMinSel;
mouseIch = min(mouseIch, ped->cch);
}
if (ped->fFocus) {
// Only do this if we have the focus since a clever app may not
// want to give us the focus at the SetFocus call above.
if (!(virtKeyDown & MK_SHIFT)) {
// If shift key isn't down, move caret to mouse point and
// clear old selection
newMinSel = newMaxSel = ped->ichCaret = mouseIch;
} else {
// Shiftkey is down so we want to maintain the current
// selection (if any) and just extend or reduce it
if (ped->ichMinSel == ped->ichCaret)
newMinSel = ped->ichCaret = mouseIch;
else
newMaxSel = ped->ichCaret = mouseIch;
}
ped->ichStartMinSel = ped->ichStartMaxSel = 0;
InitDragSelect:
ped->fMouseDown = FALSE;
NtUserSetCapture(ped->hwnd);
ped->fMouseDown = TRUE;
changeSelection = TRUE;
}
break;
case WM_LBUTTONUP:
if (ped->fMouseDown) {
ped->fMouseDown = FALSE;
NtUserReleaseCapture();
}
break;
}
if (changeSelection) {
SLScrollText(ped,hdc);
SLChangeSelection(ped, hdc, newMinSel, newMaxSel);
}
ECReleaseEditDC(ped, hdc, FALSE);
}
/***************************************************************************\
* SLPaint AorW
*
* Handles painting of the edit control window. Draws the border if
* necessary and draws the text in its current state.
*
* History:
\***************************************************************************/
void SLPaint(
PED ped,
HDC hdc)
{
HWND hwnd = ped->hwnd;
HBRUSH hBrushRemote;
RECT rcEdit;
HANDLE hOldFont;
/*
* Had to put in hide/show carets. The first one needs to be done before
* beginpaint to correctly paint the caret if part is in the update region
* and part is out. The second is for 1.03 compatibility. It breaks
* micrografix's worksheet edit control if not there.
*/
NtUserHideCaret(hwnd);
if (_IsWindowVisible(ped->pwnd)) {
/*
* Erase the background since we don't do it in the erasebkgnd message.
*/
hBrushRemote = ECGetBrush(ped, hdc);
_GetClientRect(ped->pwnd, (LPRECT)&rcEdit);
FillRect(hdc, &rcEdit, hBrushRemote);
if (ped->fFlatBorder)
{
RECT rcT;
_GetClientRect(ped->pwnd, &rcT);
DrawFrame(hdc, &rcT, 1, DF_WINDOWFRAME);
}
if (ped->hFont != NULL) {
/*
* We have to select in the font since this may be a subclassed dc
* or a begin paint dc which hasn't been initialized with out fonts
* like ECGetEditDC does.
*/
hOldFont = SelectObject(hdc, ped->hFont);
}
SLDrawText(ped, hdc, 0);
if (ped->hFont != NULL && hOldFont != NULL) {
SelectObject(hdc, hOldFont);
}
}
NtUserShowCaret(hwnd);
}
/***************************************************************************\
* SLSetFocus AorW
*
* Gives the edit control the focus and notifies the parent
* EN_SETFOCUS.
*
* History:
\***************************************************************************/
void SLSetFocus(
PED ped)
{
HDC hdc;
BOOL bDoCaret;
if (!ped->fFocus) {
ped->fFocus = TRUE; /* Set focus */
/*
* We don't want to muck with the caret since it isn't created.
*/
hdc = ECGetEditDC(ped, TRUE);
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
bDoCaret = (UINT)(* ped->lpfnCharset)(ped, EDITINTL_SETFOCUS, hdc);
} else
#endif
{
bDoCaret = TRUE;
}
/*
* Show the current selection if necessary.
*/
if (!ped->fNoHideSel)
SLDrawText(ped, hdc, 0);
/*
* Create the caret. Add in the +1 because we have an extra pixel for
* highlighting around the text. If the font is at least as wide as the
* system font, use a wide caret else use a 1 pixel wide caret.
*/
if (bDoCaret) {
NtUserCreateCaret(ped->hwnd, (HBITMAP)NULL,
ECGetCaretWidth(ped->cxSysCharWidth <= ped->aveCharWidth),
ped->lineHeight );
}
SLSetCaretPosition(ped, hdc);
ECReleaseEditDC(ped, hdc, TRUE);
NtUserShowCaret(ped->hwnd);
}
/*
* Notify parent we have the focus
*/
ECNotifyParent(ped, EN_SETFOCUS);
}
/***************************************************************************\
* SLKillFocus AorW
*
* The edit control loses the focus and notifies the parent via
* EN_KILLFOCUS.
*
* History:
\***************************************************************************/
void SLKillFocus(
PED ped,
HWND newFocusHwnd)
{
BOOL bKillCaret;
if (ped->fFocus) {
/*
* Destroy the caret (Win31/Chicago hides it first)
*/
#ifdef SUPPORT_LPK
if (ped->lpfnCharset)
bKillCaret = (UINT)(* ped->lpfnCharset)(ped, EDITINTL_SETFOCUS, (HDC)NULL);
else
#endif
bKillCaret = TRUE;
if (bKillCaret) {
NtUserDestroyCaret();
}
ped->fFocus = FALSE; /* Clear focus */
/*
* Do this only if we still have the focus. But we always notify the
* parent that we lost the focus whether or not we originally had the
* focus.
*/
/*
* Hide the current selection if needed
*/
if (!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel)) {
NtUserInvalidateRect(ped->hwnd, NULL, FALSE);
#if 0
SLSetSelection(ped, ped->ichCaret, ped->ichCaret);
#endif
}
}
/*
* If we aren't a combo box, notify parent that we lost the focus.
*/
if (!ped->listboxHwnd)
ECNotifyParent(ped, EN_KILLFOCUS);
else {
/*
* This editcontrol is part of a combo box and is losing the focus. If
* the focus is NOT being sent to another control in the combo box
* window, then it means the combo box is losing the focus. So we will
* notify the combo box of this fact.
*/
if ((newFocusHwnd == NULL) ||
(!IsChild(ped->hwndParent, newFocusHwnd))) {
// Excel has a slaker in it's midst. They're not using our combo
// boxes, but they still expect to get all the internal messages
// that we give to OUR comboboxes. And they expect them to be at
// the same offset from WM_USER as they were in 3.1.
// (JEFFBOG - 01/26/94)
/*
* Windows NT won't fix the bug described above: it only applies
* to old 16-bit excel, and WOW converts msgs to Win3.1 values.
*/
/*
* Focus is being sent to a window which is not a child of the combo
* box window which implies that the combo box is losing the focus.
* Send a message to the combo box informing him of this fact so
* that he can clean up...
*/
SendMessage(ped->hwndParent, CBEC_KILLCOMBOFOCUS, 0, 0L);
}
}
}
/***************************************************************************\
*
* SLPaste()
*
* Does actual text paste and update.
*
\***************************************************************************/
void SLPaste(PED ped)
{
HDC hdc;
//
// Insert contents of clipboard, after unhilighting current selection
// and deleting it.
//
ECDeleteText(ped);
SLPasteText(ped);
//
// Update display
//
ECNotifyParent(ped, EN_UPDATE);
hdc = ECGetEditDC(ped,FALSE);
SLScrollText(ped, hdc);
SLDrawText(ped, hdc, 0);
ECReleaseEditDC(ped,hdc,FALSE);
//
// Tell parent our text contents changed.
//
ECNotifyParent(ped, EN_CHANGE);
}
/***************************************************************************\
* SLEditWndProc
*
* Class procedure for all single line 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
* EC (edit control) prefixes all common handlers.
*
* The SLEditWndProc only handles messages specific to single line edit
* controls.
*
* 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:
\***************************************************************************/
LONG SLEditWndProc(
HWND hwnd,
PED ped,
UINT message,
DWORD wParam,
LONG lParam)
{
HDC hdc;
PAINTSTRUCT ps;
POINT pt;
/*
* Dispatch the various messages we can receive
*/
switch (message) {
case WM_CHAR:
/*
* wParam - the value of the key
lParam - modifiers, repeat count etc (not used)
*/
if (!ped->fEatNextChar)
SLChar(ped, wParam);
else
ped->fEatNextChar = FALSE;
break;
case WM_ERASEBKGND:
/*
* wParam - device context handle
* lParam - not used
* We do nothing on this message and we don't want DefWndProc to do
* anything, so return 1
*/
return (1L);
break;
case WM_GETDLGCODE: {
LONG code = DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS;
/*
* If this is a WM_SYSCHAR message generated by the UNDO keystroke
* we want this message so we can EAT IT in "case WM_SYSCHAR:"
*/
if (lParam) {
switch (((LPMSG)lParam)->message) {
case WM_SYSCHAR:
if ((HIWORD(((LPMSG)lParam)->lParam) & SYS_ALTERNATE) &&
((WORD)wParam == VK_BACK)) {
code |= DLGC_WANTMESSAGE;
}
break;
case WM_KEYDOWN:
if (( (((WORD)wParam == VK_RETURN) ||
((WORD)wParam == VK_ESCAPE)) &&
(ped->listboxHwnd) &&
TestWF(ValidateHwnd(ped->hwndParent), CBFDROPDOWN) &&
SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L))) {
code |= DLGC_WANTMESSAGE;
}
break;
}
}
return code;
}
break;
#ifdef SUPPORT_LPK // must add these msgs to server.c gawEditWndProc[]
case WM_KEYUP:
case WM_SYSKEYUP:
/*
* If an intl LPK is present, give it first crack at the keys
* if it returns zero, dont process. Note that we DON'T give it
* WM_CHAR messages. It's not supposed to be to build strings
* but to process control keys.
*
* keyup msgs : didn't process here before, always jump to ECDefWndProc).
* syskeydown : only show LPK if NOT for combo box
* keydown : if LPK says reject, dont process. if this screws
* something because the lpk processes something it
* shouldn't, it's the lpk's problem, not edit's.
*/
if (ped->lpfnCharset) {
(* ped->lpfnCharset)(ped, EDITINTL_KEYMESSAGE, message,
wParam, lParam);
goto PassToDefaultWindowProc;
}
#endif
case WM_KEYDOWN:
/*
* wParam - virt keycode of the given key
* lParam - modifiers such as repeat count etc. (not used)
*/
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
if ((* ped->lpfnCharset)(ped, EDITINTL_KEYMESSAGE, message,
wParam, lParam) == 0) {
break;
}
}
#endif
SLKeyDown(ped, wParam, 0);
break;
case WM_KILLFOCUS:
/*
* wParam - handle of the window that receives the input focus
lParam - not used
*/
SLKillFocus(ped, (HWND)wParam);
break;
case WM_CAPTURECHANGED:
if (ped->fMouseDown)
ped->fMouseDown = FALSE;
break;
case WM_MOUSEMOVE:
UserAssert(ped->fMouseDown);
/*
* FALL THRU
*/
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
/*
* wParam - contains a value that indicates which virtual keys are down
* lParam - contains x and y coords of the mouse cursor
*/
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
SLMouseMotion(ped, message, (UINT)wParam, &pt);
break;
case WM_CREATE:
/*
* wParam - handle to window being created
lParam - points to a CREATESTRUCT that contains copies of parameters
passed to the CreateWindow function.
*/
return (SLCreate(hwnd, ped, (LPCREATESTRUCT)lParam));
break;
case WM_PRINTCLIENT:
// wParam -- can be hdc from subclassed paint
// lParam -- unused
SLPaint(ped, (HDC) wParam);
break;
case WM_PAINT:
/*
* wParam -- can be hdc from subclassed paint
* lParam -- unused
*/
if (wParam)
hdc = (HDC) wParam;
else {
// this hide/show caret is outside Begin/EndPaint to handle the
// case when the caret is half in/half out of the update region
NtUserHideCaret(ped->hwnd);
hdc = NtUserBeginPaint(ped->hwnd, &ps);
}
if (_IsWindowVisible(ped->pwnd))
SLPaint(ped, hdc);
if (!wParam) {
NtUserEndPaint(ped->hwnd, &ps);
NtUserShowCaret(ped->hwnd);
}
break;
case WM_PASTE:
/*
* wParam - not used
* lParam - not used
*/
if (!ped->fReadOnly)
SLPaste(ped);
break;
case WM_SETFOCUS:
/*
* wParam - handle of window that loses the input focus (may be NULL)
lParam - not used
*/
SLSetFocus(ped);
break;
case WM_SIZE:
/*
* wParam - defines the type of resizing fullscreen, sizeiconic,
sizenormal etc.
lParam - new width in LOWORD, new height in HIGHWORD of client area
*/
ECSize(ped, NULL, TRUE);
return 0L;
case WM_SYSKEYDOWN:
/*
* wParam -- virtual key code
* lParam -- modifiers
*/
/*
* Are we in a combobox with the Alt key down?
*/
if (ped->listboxHwnd && (lParam & 0x20000000L)) {
/*
* Handle Combobox support. We want alt up or down arrow to behave
* like F4 key which completes the combo box selection
*/
if (lParam & 0x1000000) {
/*
* This is an extended key such as the arrow keys not on the
* numeric keypad so just drop the combobox.
*/
if (wParam == VK_DOWN || wParam == VK_UP)
goto DropCombo;
else
goto foo;
}
if (!(GetKeyState(VK_NUMLOCK) & 1) &&
(wParam == VK_DOWN || wParam == VK_UP)) {
/*
* NUMLOCK is up and the keypad up or down arrow hit:
* eat character generated by keyboard driver.
*/
ped->fEatNextChar = TRUE;
} else {
goto foo;
}
DropCombo:
if (SendMessage(ped->hwndParent,
CB_GETEXTENDEDUI, 0, 0) & 0x00000001) {
/*
* Extended ui doesn't honor VK_F4.
*/
if (SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0))
return(SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 0, 0));
else
return (SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 1, 0));
} else
return (SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0));
}
foo:
if (wParam == VK_BACK) {
SendMessage(ped->hwnd, WM_UNDO, 0, 0L);
break;
}
else
#ifdef SUPPORT_LPK
if (ped->lpfnCharset) {
(* ped->lpfnCharset)(ped, EDITINTL_KEYMESSAGE, message,
wParamLo, lParam);
}
}
#endif
goto PassToDefaultWindowProc;
break;
case EM_GETLINE:
/*
* wParam - line number to copy (always the first line for SL)
* lParam - buffer to copy text to. FIrst word is max # of bytes to copy
*/
return ECGetText(ped, (*(LPWORD)lParam), (LPSTR)lParam, FALSE);
case EM_LINELENGTH:
/*
* wParam - ignored
* lParam - ignored
*/
return (LONG)ped->cch;
break;
case EM_SETSEL:
/*
* wParam -- start pos
* lParam -- end pos
*/
SLSetSelection(ped, wParam, lParam);
break;
case EM_REPLACESEL:
/*
* wParam - flag for 4.0+ apps saying whether to clear undo
* lParam - points to a null terminated string of replacement text
*/
SLReplaceSel(ped, (LPSTR)lParam);
if (!ped->f40Compat || !wParam)
ECEmptyUndo(Pundo(ped));
break;
case EM_GETFIRSTVISIBLELINE:
/*
* wParam - not used
* lParam - not used
*
* effects: Returns the first visible line for single line edit controls.
*/
return ped->ichScreenStart;
break;
case EM_POSFROMCHAR:
//
// wParam -- char index in text
// lParam -- not used
// This function returns the (x,y) position of the character.
// y is always 0 for single.
//
case EM_CHARFROMPOS:
//
// wParam -- unused
// lParam -- pt in edit client coords
// This function returns
// LOWORD: the position of the _closest_ char
// to the passed in point.
// HIWORD: the index of the line (always 0 for single)
{
LONG xyPos;
hdc = ECGetEditDC(ped, TRUE);
if (message == EM_POSFROMCHAR)
xyPos = MAKELONG(SLIchToLeftXPos(ped, hdc, wParam), 0);
else {
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
xyPos = SLMouseToIch(ped, hdc, &pt);
}
ECReleaseEditDC(ped, hdc, TRUE);
return((LRESULT)xyPos);
break;
}
case WM_UNDO:
case EM_UNDO:
SLUndo(ped);
break;
#if 0
case WM_NCPAINT: // not in server.c gawEditWndProc[] anyway.
/*
* LATER - This is an NT optimization. It needs to be revisited
* for validity once all of the Chicago changes are done - Johnl
*/
pwnd = (PWND)HtoP(hwnd);
/*
* Check to see if this window has any non-client areas that
* would be painted. If not, don't bother calling DefWindowProc()
* since it'll be a wasted c/s transition.
*/
if (!ped->fBorder &&
TestWF(pwnd, WEFDLGMODALFRAME) == 0 &&
!TestWF(pwnd, (WFMPRESENT | WFVPRESENT | WFHPRESENT)) &&
TestWF(pwnd, WFSIZEBOX) == 0) {
break;
} else {
goto PassToDefaultWindowProc;
}
break;
#endif
default:
PassToDefaultWindowProc:
return DefWindowProcWorker(ped->pwnd, message, wParam, lParam, ped->fAnsi);
break;
} /* switch (message) */
return 1L;
} /* SLEditWndProc */