|
|
#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "edit.h"
//---------------------------------------------------------------------------//
//
// Language pack notes:
// With the language pack loaded all positional processing is based on
// ped->xOffset rather than ped->ichScreenStart. The non-lpk optimisation of
// maintaining ped->ichScreenStart doesn't work because of the
// glyph reordering features of complex scripts.
//
//---------------------------------------------------------------------------//
//
// Forwards
//
VOID EditSL_ChangeSelection(PED, HDC, ICH, ICH); VOID EditSL_DrawLine(PED, HDC, int, int, ICH, int, BOOL); BOOL EditSL_Undo(PED);
//---------------------------------------------------------------------------//
//
typedef BOOL (*FnGetTextExtentPoint)(HDC, PVOID, int, LPSIZE);
//---------------------------------------------------------------------------//
INT EditSL_CalcStringWidth(PED ped, HDC hdc, ICH ich, ICH cch) { if (cch == 0) { return 0; }
if (ped->charPasswordChar) { return cch * ped->cPasswordCharWidth; } else { SIZE size;
if (ped->fNonPropFont && !ped->fDBCS) { size.cx = cch * ped->aveCharWidth; } else { PSTR pText = Edit_Lock(ped);
if (ped->fAnsi) { GetTextExtentPointA(hdc, (LPSTR)(pText + ich), cch, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)pText + ich, cch, &size); }
Edit_Unlock(ped); }
return size.cx - ped->charOverhang; } }
//---------------------------------------------------------------------------//
//
// EditSL_CalcXOffsetLeft
//
// Calculates the starting offset for left-aligned strings.
//
INT EditSL_CalcXOffsetLeft(PED ped, HDC hdc, ICH ich) { int cch = (int)(ich - ped->ichScreenStart);
if (cch <= 0) { return 0; }
return EditSL_CalcStringWidth(ped, hdc, ped->ichScreenStart, cch); }
//---------------------------------------------------------------------------//
//
// EditSL_CalcXOffsetSpecial
//
// Calculates the horizontal offset (indent) required for right or center
// justified lines.
//
INT EditSL_CalcXOffsetSpecial(PED ped, HDC hdc, ICH ich) { PSTR pText; ICH cch, ichStart = ped->ichScreenStart; int cx;
//
// Calc the number of characters from start to right end.
//
pText = Edit_Lock(ped); cch = Edit_CchInWidth(ped, hdc, (LPSTR)(pText + ichStart * ped->cbChar), ped->cch - ichStart, ped->rcFmt.right - ped->rcFmt.left, TRUE); Edit_Unlock(ped);
//
// Once the last character of the string has been scrolled out of
// the view, use normal offset calculation.
//
if (ped->ichScreenStart + cch < ped->cch) { return EditSL_CalcXOffsetLeft(ped, hdc, ich); }
cx = ped->rcFmt.right - ped->rcFmt.left - EditSL_CalcStringWidth(ped, hdc, ichStart, cch);
if (ped->format == ES_CENTER) { cx = max(0, cx / 2); } else if (ped->format == ES_RIGHT) { //
// Subtract 1 so that the 1 pixel wide cursor will be in the visible
// region on the very right side of the screen, mle does this.
//
cx = max(0, cx - 1); }
return cx + EditSL_CalcStringWidth(ped, hdc, ichStart, ich - ichStart); }
//---------------------------------------------------------------------------//
//
// EditSL_SetCaretPosition AorW
//
// If the window has the focus, find where the caret belongs and move
// it there.
//
VOID EditSL_SetCaretPosition(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) { SetCaretPos(-20000, -20000); return; }
xPosition = EditSL_IchToLeftXPos(ped, hdc, ped->ichCaret);
//
// Don't let caret go out of bounds of edit control if there is too much
// text.
//
if (ped->pLpkEditCallout) { xPosition += ped->iCaretOffset; xPosition = max(xPosition , 0); xPosition = min(xPosition, ped->rcFmt.right - 1 - ((ped->cxSysCharWidth > ped->aveCharWidth) ? 1 : 2)); } else { xPosition = min(xPosition, ped->rcFmt.right - ((ped->cxSysCharWidth > ped->aveCharWidth) ? 1 : 2)); }
SetCaretPos(xPosition, ped->rcFmt.top);
//
// FE_IME EditSL_SetCaretPosition - ECImmSetCompostionWindow( CFS_POINT )
//
if (g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0))) { Edit_ImmSetCompositionWindow(ped, xPosition, ped->rcFmt.top); } }
//---------------------------------------------------------------------------//
//
// EditSL_IchToLeftXPos 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.
//
INT EditSL_IchToLeftXPos(PED ped, HDC hdc, ICH ich) { int textExtent; PSTR pText; SIZE size; int cchDiff;
if (ped->pLpkEditCallout) { pText = Edit_Lock(ped); textExtent = ped->pLpkEditCallout->EditIchToXY((PED0)ped, hdc, pText, ped->cch, ich); Edit_Unlock(ped);
return textExtent;
}
//
// 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); }
if (ped->format != ES_LEFT) { return (ped->rcFmt.left + EditSL_CalcXOffsetSpecial(ped, hdc, ich)); }
//
// Caret position /w DBCS text, we can not optimize...
//
if (ped->fNonPropFont && !ped->fDBCS) { 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 = Edit_Lock(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 { 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; } }
Edit_Unlock(ped);
return (ped->rcFmt.left + textExtent - (textExtent ? ped->charOverhang : 0)); }
//---------------------------------------------------------------------------//
//
// EditSL_SetSelection AorW
//
// Sets the PED to have the new selection specified.
//
VOID EditSL_SetSelection(PED ped, ICH ichSelStart, ICH ichSelEnd) { HDC hdc = Edit_GetDC(ped, FALSE);
if (ichSelStart == 0xFFFFFFFF) { //
// Set no selection if we specify -1
//
ichSelStart = ichSelEnd = ped->ichCaret; }
//
// Bounds ichSelStart, ichSelEnd are checked in EditSL_ChangeSelection...
//
EditSL_ChangeSelection(ped, hdc, ichSelStart, ichSelEnd);
//
// Put the caret at the end of the selected text
//
ped->ichCaret = ped->ichMaxSel;
EditSL_SetCaretPosition(ped, hdc);
//
// We may need to scroll the text to bring the caret into view...
//
EditSL_ScrollText(ped, hdc);
Edit_ReleaseDC(ped, hdc, FALSE); }
//---------------------------------------------------------------------------//
VOID EditSL_GetClipRect(PED ped, HDC hdc, ICH ichStart, int iCount, LPRECT lpClipRect) { int iStCount; PSTR pText;
if (ped->pLpkEditCallout) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_GetClipRect - Error - Invalid call with language pack loaded"); memset(lpClipRect, 0, SIZEOF(RECT));
return; }
CopyRect(lpClipRect, &ped->rcFmt);
pText = Edit_Lock(ped);
//
// Calculates the starting pos for this piece of text
//
if ((iStCount = (int)(ichStart - ped->ichScreenStart)) > 0) { if (ped->format == ES_LEFT) { lpClipRect->left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { //
// Reset the values to visible portions
//
iCount -= (ped->ichScreenStart - ichStart); ichStart = ped->ichScreenStart; }
if (ped->format != ES_LEFT) { lpClipRect->left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); }
if (iCount < 0) { //
// This is not in the visible area of the edit control, so return
// an empty rect.
//
SetRectEmpty(lpClipRect); Edit_Unlock(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; }
Edit_Unlock(ped); }
//---------------------------------------------------------------------------//
//
// EditSL_LpkEditDrawText
//
// lpk!EditDrawText always sets the BkMode for single line edits to OPAQUE.
// This causes painting problems for read-only edits in property sheets.
// Unfortunately, lpk.dll can't be changed since it would break the user32
// edit, so I'm faking lpk!EditDrawText into thinking this isn't a single
// line edit.
//
__inline VOID EditSL_LpkEditDrawText(PED ped, HDC hdc, PSTR pText) { BOOL fSingleSave; fSingleSave = ped->fSingle; ped->fSingle = FALSE; ped->pLpkEditCallout->EditDrawText((PED0)ped, hdc, pText, ped->cch, ped->ichMinSel, ped->ichMaxSel, ped->rcFmt.top); ped->fSingle = fSingleSave; }
//---------------------------------------------------------------------------//
//
// EditSL_ChangeSelection 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.
//
VOID EditSL_ChangeSelection(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);
//
// To avoid position to half of DBCS, check and ajust position if necessary
//
// We check ped->fDBCS and ped->fAnsi though Edit_AdjustIch checks these bits.
// We're worrying about the overhead of EcLock and EcUnlock.
//
if (ped->fDBCS && ped->fAnsi) { PSTR pText;
pText = Edit_Lock(ped); ichNewMinSel = Edit_AdjustIch( ped, pText, ichNewMinSel ); ichNewMaxSel = Edit_AdjustIch( ped, pText, ichNewMaxSel ); Edit_Unlock(ped); }
//
// 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->hwnd) && (ped->fFocus || ped->fNoHideSel)) { SELBLOCK Blk[2]; int i; RECT rc;
if (ped->fFocus) { HideCaret(ped->hwnd); }
if (ped->pLpkEditCallout) { //
// The language pack handles display while complex script support present
//
PSTR pText; HBRUSH hbr = NULL; BOOL fNeedDelete = FALSE;
//
// Give user a chance to manipulate the DC
//
hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); FillRect(hdc, &ped->rcFmt, hbr); pText = Edit_Lock(ped); EditSL_LpkEditDrawText(ped, hdc, pText); Edit_Unlock(ped); if (hbr && fNeedDelete) { DeleteObject(hbr); } } else { Blk[0].StPos = ichOldMinSel; Blk[0].EndPos = ichOldMaxSel; Blk[1].StPos = ped->ichMinSel; Blk[1].EndPos = ped->ichMaxSel;
if (Edit_CalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel, (LPSELBLOCK)&Blk[0], (LPSELBLOCK)&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) { EditSL_GetClipRect(ped, hdc, Blk[i].StPos, Blk[i].EndPos - Blk[i].StPos, (LPRECT)&rc); EditSL_DrawLine(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.
//
EditSL_SetCaretPosition(ped, hdc);
if (ped->fFocus) { ShowCaret(ped->hwnd); } } }
//---------------------------------------------------------------------------//
//
// EditSL_DrawLine()
//
// This draws the line starting from ichStart, iCount number of characters;
// fSelStatus is TRUE if we're to draw the text as selected.
//
VOID EditSL_DrawLine(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 = NULL; BOOL fNeedDelete = FALSE; HRESULT hr;
if (ped->pLpkEditCallout) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_DrawLine - Error - Invalid call with language pack loaded"); return; }
//
// Anything to draw?
//
// PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are
// slightly different.
if (xClipStPos >= xClipEndPos || !IsWindowVisible(ped->hwnd) ) { return; }
if (ped->fAnsi && ped->fDBCS) { PSTR pT,pTOrg; int iTCount;
pText = Edit_Lock(ped); ichNewStart = 0; if (ichStart > 0) { pT = pText + ichStart; ichNewStart = ichStart;
while (ichNewStart && (ichStart - ichNewStart < ped->wMaxNegCcharPos)) { pT = Edit_AnsiPrev(ped, pText, pT); ichNewStart = (ICH)(pT - pText); if (!ichNewStart) { break; } }
//
// B#16152 - win95.
// In case of T2, SLE always set an additional margin
// to erase a character (iCount == 0 case), using aveCharWidth.
// It erases unexpected an extra char if we don't use ichNewStart
// and it happens when wMaxNegCcharPos == 0.
//
if (ped->wMaxNegCcharPos == 0 && iCount == 0) { pT = Edit_AnsiPrev(ped, pText, pT); ichNewStart = (ICH)(pT - pText); } }
iTCount = 0; if (ichStart + iCount < ped->cch) { pTOrg = pT = pText + ichStart + iCount; while ((iTCount < (int)ped->wMaxNegAcharPos) && (ichStart + iCount + iTCount < ped->cch)) { pT = Edit_AnsiNext(ped, pT); iTCount = (int)(pT - pTOrg); } }
Edit_Unlock(ped); iCount = (int)(min(ichStart+iCount+iTCount, ped->cch) - ichNewStart); } else { //
// 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
//
Edit_SetClip(ped, hdc, TRUE);
pText = Edit_Lock(ped);
//
// Calculate the starting pos for this piece of text
//
if (ped->format == ES_LEFT) { if (iStCount = (int)(ichStart - ped->ichScreenStart)) { rc.left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { rc.left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); }
//
// Set the background mode before calling NtUserGetControlBrush so that the app
// can change it to TRANSPARENT if it wants to.
//
SetBkMode(hdc, OPAQUE);
hr = E_FAIL; #ifdef _USE_DRAW_THEME_TEXT_
if ( ped->hTheme ) { INT iState; INT iProp; COLORREF clrBk; COLORREF clrText;
iState = fSelStatus ? ETS_SELECTED : Edit_GetStateId(ped); iProp = fSelStatus ? TMT_HIGHLIGHT : TMT_FILLCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrBk); if ( SUCCEEDED(hr) ) { iProp = fSelStatus ? TMT_HIGHLIGHTTEXT : TMT_TEXTCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrText);
if ( SUCCEEDED(hr) ) { hbrBack = CreateSolidBrush(clrBk); fNeedDelete = TRUE; rgbSaveBk = SetBkColor(hdc, clrBk); rgbSaveText = SetTextColor(hdc, clrText); } } } #endif // _USE_DRAW_THEME_TEXT_
if ( !ped->hTheme || FAILED(hr) ) { if (fSelStatus) { //
// if we're not themed or we are themed but failed
// to get the highlight and highlighttext colors
//
// use normal colors
//
hbrBack = GetSysColorBrush(COLOR_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 = Edit_GetBrush(ped, hdc, &fNeedDelete); rgbSaveBk = GetBkColor(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); SetBkColor(hdc, rgbSaveBk);
if (hbrBack && fNeedDelete) { DeleteObject(hbrBack); }
sldl_errorexit:
Edit_Unlock(ped); }
//---------------------------------------------------------------------------//
//
// EditSL_GetBlkEnd 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?
//
INT EditSL_GetBlkEnd(PED ped, ICH ichStart, ICH ichEnd, BOOL *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); }
//---------------------------------------------------------------------------//
//
// EditSL_DrawCueBannerText (Unicode Only!)
//
// This function is called by EditSL_DrawText to display the cue banner text in
// the edit box.
//
// Note:
// May need to call pLpkEditCallout to support complex scripts.
//
VOID EditSL_DrawCueBannerText(PED ped, HDC hdc, RECT rc) { //
// Draw the overlay of the cue banner text.
// Only draw this text if:
// 1. has cue banner text to display
// 2. the edit box is empty,
// 3. does not have focus,
// 4. is not disabled
// 5. and is not read only
//
if (ped->pszCueBannerText && ped->cch == 0 && !ped->fFocus && !ped->fDisabled && !ped->fReadOnly) { COLORREF crOldColor; UINT iOldAlign; //
// Setup the font to be light gray
// NOTE: Should this be read from the theme manager?
//
crOldColor = SetTextColor(hdc, GetSysColor(COLOR_BTNSHADOW)); //
// Setup the alignment for the text to display.
// We will match our alignment with the alignment that is
// actually used for text in the edit control
//
switch (ped->format) { case ES_LEFT: iOldAlign = SetTextAlign(hdc, TA_LEFT); break; case ES_RIGHT: iOldAlign = SetTextAlign(hdc, TA_RIGHT); break; case ES_CENTER: iOldAlign = SetTextAlign(hdc, TA_CENTER); break; }
// Draw the text to the box:
ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &(ped->rcFmt), ped->pszCueBannerText, // Text
wcslen(ped->pszCueBannerText), // Size of text
NULL);
//
// Reset the alignment
//
SetTextAlign(hdc, iOldAlign);
//
// Reset the color back:
//
SetTextColor(hdc, crOldColor); } }
//---------------------------------------------------------------------------//
//
// EditSL_DrawText 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 Edit_GetDC so that the caret and such are properly hidden.
//
VOID EditSL_DrawText(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; HBRUSH hbr = NULL; BOOL fNeedDelete = FALSE;
//
// PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are
// slightly different.
//
if (!IsWindowVisible(ped->hwnd)) { return; }
if (ped->pLpkEditCallout) { //
// The language pack handles display while complex script support present
//
//
// Give user a chance to manipulate the DC
//
hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); pText = Edit_Lock(ped); EditSL_LpkEditDrawText(ped, hdc, pText); Edit_Unlock(ped); EditSL_SetCaretPosition(ped, hdc); if (hbr && fNeedDelete) { DeleteObject(hbr); }
return; }
//
// When drawing the entire visible content of special-aligned sle
// erase the view.
//
if (ped->format != ES_LEFT && ichStart == 0) { hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); FillRect(hdc, &ped->rcFmt, hbr); if (hbr && fNeedDelete) { DeleteObject(hbr); } }
pText = Edit_Lock(ped);
if (ichStart < ped->ichScreenStart) { #if DBG
ICH ichCompare = Edit_AdjustIch(ped, pText, ped->ichScreenStart); UserAssert(ichCompare == ped->ichScreenStart); #endif
ichStart = ped->ichScreenStart; } else if (ped->fDBCS && ped->fAnsi) { //
// If ichStart stays on trailing byte of DBCS, we have to
// adjust it.
//
ichStart = Edit_AdjustIch(ped, pText, ichStart); }
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.
//
cchToDraw = Edit_CchInWidth(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 (ped->format == ES_LEFT) { if (iStCount = (int)(ichStart - ped->ichScreenStart)) { rc.left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { rc.left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); }
//
// 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 = EditSL_GetBlkEnd(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 (fDrawLeftMargin) { fDrawLeftMargin = FALSE; rc.left -= ped->wLeftMargin; if (rc.right < rc.left) { rc.right = rc.left; } }
EditSL_DrawLine(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; } } Edit_Unlock(ped);
//
// Check if anything to be erased on the right hand side
//
if (fDrawEndOfLineStrip && (rc.left < (rc.right = (ped->rcFmt.right+ped->wRightMargin)))) { EditSL_DrawLine(ped, hdc, rc.left, rc.right, ichStart, 0, FALSE); }
EditSL_SetCaretPosition(ped, hdc);
//
// Call the function to display the cue banner text into the edit box
//
EditSL_DrawCueBannerText(ped, hdc, rc); }
//---------------------------------------------------------------------------//
//
// EditSL_ScrollText 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.
//
BOOL EditSL_ScrollText(PED ped, HDC hdc) { PSTR pTextScreenStart; ICH scrollAmount; ICH newScreenStartX = ped->ichScreenStart; ICH cch; BOOLEAN fAdjustNext = FALSE;
if (!ped->fAutoHScroll) { return FALSE; }
if (ped->pLpkEditCallout) { BOOL fChanged;
//
// With complex script glyph reordering, use lpk to do horz scroll
//
pTextScreenStart = Edit_Lock(ped); fChanged = ped->pLpkEditCallout->EditHScroll((PED0)ped, hdc, pTextScreenStart); Edit_Unlock(ped);
if (fChanged) { EditSL_DrawText(ped, hdc, 0); }
return fChanged; }
//
// 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 = Edit_Lock(ped);
scrollAmount = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->ichCaret, (ped->rcFmt.right - ped->rcFmt.left) / 4, FALSE);
newScreenStartX = ped->ichCaret - scrollAmount; Edit_Unlock(ped); } else if (ped->ichCaret != ped->ichScreenStart) { pTextScreenStart = Edit_Lock(ped); pTextScreenStart += ped->ichScreenStart * ped->cbChar;
cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->ichCaret - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE);
if (cch < ped->ichCaret - ped->ichScreenStart) { fAdjustNext = TRUE;
//
// 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 = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE);
if (newScreenStartX > (ped->cch - cch)) { newScreenStartX = ped->cch - cch; } } else if (ped->format != ES_LEFT) { cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE);
//
// Scroll the text hidden behind the left border back
// into view.
//
if (ped->ichScreenStart == ped->cch - cch) { pTextScreenStart -= ped->ichScreenStart * ped->cbChar; cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch, ped->rcFmt.right - ped->rcFmt.left, FALSE);
newScreenStartX = ped->cch - cch; } }
Edit_Unlock(ped); }
//
// Adjust newScreenStartX
//
if (ped->fAnsi && ped->fDBCS) { newScreenStartX = (fAdjustNext ? Edit_AdjustIchNext : Edit_AdjustIch)(ped, Edit_Lock(ped), newScreenStartX); Edit_Unlock(ped); }
if (ped->ichScreenStart != newScreenStartX) { //
// Check if we have to wipe out the left margin
//
if (ped->wLeftMargin && (ped->ichScreenStart == 0)) { RECT rc; HBRUSH hBrush = NULL; BOOL fNeedDelete = FALSE;
hBrush = Edit_GetBrush(ped, hdc, &fNeedDelete);
CopyRect(&rc, &ped->rcFmt); InflateRect(&rc, 0, 1); rc.right = rc.left; rc.left -= ped->wLeftMargin;
FillRect(hdc, &rc, hBrush); if (hBrush && fNeedDelete) { DeleteObject(hBrush); } }
ped->ichScreenStart = newScreenStartX; EditSL_DrawText(ped, hdc, 0);
//
// Caret pos is set by EditSL_DrawText().
//
return TRUE; }
return FALSE; }
//---------------------------------------------------------------------------//
//
// EditSL_InsertText 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.
//
ICH EditSL_InsertText(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 = Edit_Lock(ped); hdc = Edit_GetDC(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 = Edit_CchInWidth(ped, hdc, lpText, cchInsert, ped->rcFmt.right - ped->rcFmt.left - textWidth, TRUE); cchInsert = min(cchInsert, cchT);
Edit_Unlock(ped); Edit_ReleaseDC(ped, hdc, TRUE); } else { cchInsert = min((unsigned)(ped->cchTextMax - ped->cch), cchInsert); } }
//
// Now try actually adding the text to the ped
//
if (cchInsert && !Edit_InsertText(ped, lpText, &cchInsert)) { Edit_NotifyParent(ped, EN_ERRSPACE); return 0; } if (cchInsert) { ped->fDirty = TRUE; }
if (cchInsert < cchInsertCopy) { //
// Notify parent that we couldn't insert all the text requested
//
Edit_NotifyParent(ped, EN_MAXTEXT); }
//
// Update selection extents and the caret position. Note that Edit_InsertText
// updates ped->ichCaret, ped->ichMinSel, and ped->ichMaxSel to all be after
// the inserted text.
//
return cchInsert; }
//---------------------------------------------------------------------------//
//
// EditSL_PasteText 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.
//
ICH EditSL_PasteText(PED ped) { HANDLE hData; LPSTR lpchClip; ICH cchAdded = 0; ICH clipLength;
if (!OpenClipboard(ped->hwnd)) { goto PasteExitNoCloseClip; } hData = GetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT); if (!hData || (GlobalFlags(hData) == GMEM_INVALID_HANDLE)) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_PasteText(): couldn't get a valid handle(%x)", hData); goto PasteExit; }
lpchClip = GlobalLock(hData); if (lpchClip == NULL) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_PasteText(): USERGLOBALLOCK(%x) failed.", hData); goto PasteExit; }
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 { 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 (EditSL_InsertText checks line length)
//
cchAdded = EditSL_InsertText(ped, lpchClip, cchAdded);
GlobalUnlock(hData);
PasteExit: CloseClipboard();
PasteExitNoCloseClip: return cchAdded; }
//---------------------------------------------------------------------------//
//
// EditSL_ReplaceSel AorW
//
// Replaces the text in the current selection with the given text.
//
VOID EditSL_ReplaceSel(PED ped, LPSTR lpText) { UINT cchText;
//
// Delete text, putting it into the clean undo buffer.
//
Edit_EmptyUndo(Pundo(ped)); Edit_DeleteText(ped);
//
// B#3356
// Some apps do "clear" by selecting all of the text, then replacing it
// with "", in which case EditSL_InsertText() 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!
//
Edit_SaveUndo(Pundo(ped), &undo, FALSE);
hwndSave = ped->hwnd; fFailed = (BOOL) !EditSL_InsertText(ped, lpText, cchText); if (!IsWindow(hwndSave)) { return; }
if (fFailed) { //
// UNDO the previous edit.
//
Edit_SaveUndo(&undo, Pundo(ped), FALSE); EditSL_Undo(ped); return; } }
//
// Success. So update the display
//
Edit_NotifyParent(ped, EN_UPDATE);
// PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are
// slightly different.
if (IsWindowVisible(ped->hwnd)) { HDC hdc;
hdc = Edit_GetDC(ped, FALSE);
if (!EditSL_ScrollText(ped, hdc)) { EditSL_DrawText(ped, hdc, 0); }
Edit_ReleaseDC(ped, hdc, FALSE); }
Edit_NotifyParent(ped, EN_CHANGE);
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); }
//---------------------------------------------------------------------------//
//
// EditSL_Char AorW
//
// Handles character input
//
VOID EditSL_Char(PED ped, DWORD keyValue) { HDC hdc; WCHAR keyPress; BOOL updateText = FALSE; HWND hwndSave = ped->hwnd; int InsertTextLen = 1; int DBCSkey;
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; }
if (g_fIMMEnabled) { Edit_InOutReconversionMode(ped, FALSE); }
switch (keyPress) { case VK_BACK: DeleteSelection: if (Edit_DeleteText(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 && (GET_STYLE(ped) & ES_NUMBER)) { if (!Edit_IsCharNumeric(ped, keyPress)) { Edit_ShowBalloonTipWrap(ped->hwnd, IDS_NUMERIC_TITLE, IDS_NUMERIC_MSG, TTI_ERROR); 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
//
// Calling PrevIch rather than just doing a decrement for VK_BACK
//
ped->ichMinSel = Edit_PrevIch( ped, NULL, ped->ichMinSel); Edit_DeleteText(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 >= 0x1E) { //
// 1E,1F are unicode block and segment separators
//
//
// Hide the cursor if typing, if the mouse is captured, do not mess with this
// as it is going to desapear forever (no WM_SETCURSOR is sent to restore it
// at the first mouse-move)
// MCostea #166951
//
if (GetCapture() == NULL) { SetCursor(NULL); }
if (g_fDBCSEnabled && ped->fAnsi && (Edit_IsDBCSLeadByte(ped,(BYTE)keyPress))) { if ((DBCSkey = DbcsCombine(ped->hwnd, keyPress)) != 0 && EditSL_InsertText(ped,(LPSTR)&DBCSkey, 2) == 2) { InsertTextLen = 2; updateText = TRUE; } else { MessageBeep(0); } } else { InsertTextLen = 1; if (EditSL_InsertText(ped, (LPSTR)&keyPress, 1)) { updateText = TRUE; } else { //
// Beep. Since we couldn't add the text
//
MessageBeep(0); } } } else { IllegalChar: MessageBeep(0); }
if (!IsWindow(hwndSave)) { return; }
break; }
if (updateText) { //
// Dirty flag (ped->fDirty) was set when we inserted text
//
Edit_NotifyParent(ped, EN_UPDATE); hdc = Edit_GetDC(ped, FALSE); if (!EditSL_ScrollText(ped, hdc)) { if (ped->format == ES_LEFT) { //
// Call EditSL_DrawText with correct ichStart
//
EditSL_DrawText(ped, hdc, max(0, (int)(ped->ichCaret - InsertTextLen - ped->wMaxNegCcharPos))); } else { //
// We can't just draw from ichStart because string may have
// shifted because of alignment.
//
EditSL_DrawText(ped, hdc, 0); } }
Edit_ReleaseDC(ped, hdc, FALSE); Edit_NotifyParent(ped, EN_CHANGE);
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } }
//---------------------------------------------------------------------------//
//
// EditSL_MoveSelectionRestricted AorW
//
// Moves the selection like Edit_MoveSelection, but also obeys limitations
// imposed by some languages such as Thai, where the cursor cannot stop
// between a character and it's attached vowel or tone marks.
//
// Only called if the language pack is loaded.
//
ICH EditSL_MoveSelectionRestricted(PED ped, ICH ich, BOOL fLeft) { PSTR pText; HDC hdc; ICH ichResult;
pText = Edit_Lock(ped); hdc = Edit_GetDC(ped, TRUE); ichResult = ped->pLpkEditCallout->EditMoveSelection((PED0)ped, hdc, pText, ich, fLeft); Edit_ReleaseDC(ped, hdc, TRUE); Edit_Unlock(ped);
return ichResult; }
//---------------------------------------------------------------------------//
void EditSL_CheckCapsLock(PED ped) { if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) { Edit_ShowBalloonTipWrap(ped->hwnd, IDS_CAPSLOCK_TITLE, IDS_CAPSLOCK_MSG, TTI_WARNING); } }
//---------------------------------------------------------------------------//
//
// EditSL_KeyDown AorW
//
// Handles cursor movement and other VIRT KEY stuff. keyMods allows
// us to make EditSL_KeyDownHandler 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.
//
VOID EditSL_KeyDown(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; }
if (ped->hwndBalloon) { Edit_HideBalloonTip(ped->hwnd); }
scState = Edit_GetModKeys(keyMods);
switch (virtKeyCode) { case VK_UP: if ( ped->listboxHwnd ) { //
// Handle Combobox support
//
fIsExtendedUI = (BOOL)SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0); fIsListVisible = (BOOL)SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0);
if (!fIsListVisible && fIsExtendedUI) { 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
//
Edit_Word(ped, ped->ichCaret, TRUE, &ped->ichCaret, NULL); } else { //
// Move caret char left
//
if (ped->pLpkEditCallout) { ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, TRUE); } else { ped->ichCaret = Edit_PrevIch(ped,NULL,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 = (BOOL)SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0); fIsListVisible = (BOOL)SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0);
if (!fIsListVisible && fIsExtendedUI) { 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
//
Edit_Word(ped, ped->ichCaret, FALSE, NULL, &ped->ichCaret); } else { //
// Move caret char right
//
if (ped->pLpkEditCallout) { ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, FALSE); } else { ped->ichCaret = Edit_NextIch(ped,NULL,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.
//
if (ped->pLpkEditCallout) { ped->ichMinSel = ped->ichCaret; ped->ichMaxSel = ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, FALSE); } else { ped->ichCaret = Edit_NextIch(ped,NULL,ped->ichCaret); ped->ichMaxSel = ped->ichMinSel = ped->ichCaret; }
EditSL_Char(ped, (UINT)VK_BACK); }
if (ped->ichMinSel != ped->ichMaxSel) { EditSL_Char(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); EditSL_Char(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) { EditSL_Char(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, 0, 0); break;
case SHFTDOWN: SendMessage(ped->hwnd, WM_PASTE, 0, 0L); break; } break;
case VK_HANJA: //
// VK_HANJA support
//
if ( HanjaKeyHandler( ped ) ) { changeSelection = TRUE; newMinSel = ped->ichCaret; newMaxSel = ped->ichCaret + (ped->fAnsi ? 2 : 1); }
break;
case VK_CAPITAL:
if (GET_STYLE(ped) & ES_PASSWORD) { EditSL_CheckCapsLock(ped); }
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 = Edit_GetDC(ped, FALSE);
//
// Scroll if needed
//
EditSL_ScrollText(ped, hdc);
if (changeSelection) { EditSL_ChangeSelection(ped, hdc, newMinSel, newMaxSel); }
if (updateText) { EditSL_DrawText(ped, hdc, 0); }
Edit_ReleaseDC(ped, hdc, FALSE); if (updateText) { Edit_NotifyParent(ped, EN_CHANGE);
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } } }
//---------------------------------------------------------------------------//
//
// EditSL_MouseToIch AorW
//
// Returns the closest cch to where the mouse point is.
//
ICH EditSL_MouseToIch(PED ped, HDC hdc, LPPOINT mousePt) { PSTR pText; int width = mousePt->x; int lastHighWidth, lastLowWidth; SIZE size; ICH cch; ICH cchLo, cchHi; LPSTR lpText; FnGetTextExtentPoint pGetTextExtentPoint;
if (ped->pLpkEditCallout) { pText = Edit_Lock(ped); cch = ped->pLpkEditCallout->EditMouseToIch((PED0)ped, hdc, pText, ped->cch, width); Edit_Unlock(ped);
return cch; }
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 = Edit_Lock(ped);
//
// Return last char in text or one plus the last char visible
//
cch = Edit_CchInWidth(ped, hdc, (LPSTR)(pText + ped->ichScreenStart * ped->cbChar), ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, TRUE) + ped->ichScreenStart;
//
// This is marked as JAPAN in Win31J. But it should be a DBCS
// issue. LiZ -- 5/5/93
// We must check DBCS Lead byte. Because Edit_AdjustIch() pick up Prev Char.
// 1993.3.9 by yutakas
//
if (ped->fAnsi && ped->fDBCS) { if (cch >= ped->cch) { cch = ped->cch; } else { if (Edit_IsDBCSLeadByte(ped,*(pText+cch))) { cch += 2; } else { cch ++; } }
Edit_Unlock(ped);
return cch; } else { Edit_Unlock(ped); if (cch >= ped->cch) { return (ped->cch); } else { return (cch + 1); } } }
if (ped->format != ES_LEFT) { width -= EditSL_CalcXOffsetSpecial(ped, hdc, ped->ichScreenStart); }
//
// 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 = Edit_Lock(ped); lpText = pText + ped->ichScreenStart * ped->cbChar;
pGetTextExtentPoint = ped->fAnsi ? (FnGetTextExtentPoint)GetTextExtentPointA : (FnGetTextExtentPoint)GetTextExtentPointW; width -= ped->rcFmt.left;
//
// If the user clicked past the end of the text, return the last character
//
cchHi = ped->cch - ped->ichScreenStart; pGetTextExtentPoint(hdc, lpText, cchHi, &size); if (size.cx <= width) { cch = cchHi; goto edAdjust; }
//
// Initialize Binary Search Bounds
//
cchLo = 0; cchHi ++; lastLowWidth = 0; lastHighWidth = size.cx;
//
// Binary search for closest char
//
while (cchLo < cchHi - 1) { cch = (cchHi + cchLo) / 2; pGetTextExtentPoint(hdc, lpText, cch, &size);
if (size.cx <= width) { cchLo = cch; lastLowWidth = size.cx; } else { cchHi = cch; lastHighWidth = size.cx; } }
//
// When the while ends, you can't know the exact position.
// Try to see if the mouse pointer was on the farest half
// of the char we got and if so, adjust cch.
//
if (cchLo == cch) { //
// Need to compare with lastHighWidth
//
if ((lastHighWidth - width) < (width - size.cx)) { cch++; } } else { //
// Need to compare with lastLowWidth
//
if ((width - lastLowWidth) < (size.cx - width)) { cch--; } }
edAdjust: //
// Avoid to point the intermediate of double byte character
//
cch = Edit_AdjustIch(ped, pText, cch + ped->ichScreenStart); Edit_Unlock(ped); return cch; }
//---------------------------------------------------------------------------//
VOID EditSL_MouseMotion(PED ped, UINT message, UINT virtKeyDown, LPPOINT mousePt) { DWORD selectionl; DWORD selectionh; BOOL changeSelection; ICH newMaxSel; ICH newMinSel; HDC hdc; ICH mouseIch; LPSTR pText;
changeSelection = FALSE;
newMinSel = ped->ichMinSel; newMaxSel = ped->ichMaxSel;
hdc = Edit_GetDC(ped, FALSE); mouseIch = EditSL_MouseToIch(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.
//
// in DBCS, we have different word breaking. LiZ -- 5/5/93
// In Hangeul Environment we use word selection feature because Hangeul
// use SPACE as word break
//
if (ped->fAnsi && ped->fDBCS) { pText = Edit_Lock(ped) + mouseIch; Edit_Word(ped, mouseIch, (Edit_IsDBCSLeadByte(ped,*pText) && mouseIch < ped->cch) ? FALSE : TRUE, &selectionl, &selectionh); Edit_Unlock(ped); } else { Edit_Word(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; Edit_Word(ped, newMaxSel, TRUE, &selectionl, &selectionh); } else { newMaxSel = ped->ichCaret = selectionh; Edit_Word(ped, newMinSel, FALSE, &selectionl, &selectionh); }
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 EditSL_WndProc 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);
Edit_Word(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; }
SetFocus(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; SetCapture(ped->hwnd); ped->fMouseDown = TRUE; changeSelection = TRUE; }
break;
case WM_LBUTTONUP: if (ped->fMouseDown) { ped->fMouseDown = FALSE; ReleaseCapture(); }
break; }
if (changeSelection) { EditSL_ScrollText(ped,hdc); EditSL_ChangeSelection(ped, hdc, newMinSel, newMaxSel); }
Edit_ReleaseDC(ped, hdc, FALSE); }
//---------------------------------------------------------------------------//
//
// EditSL_Paint AorW
//
// Handles painting of the edit control window. Draws the border if
// necessary and draws the text in its current state.
//
VOID EditSL_Paint(PED ped, HDC hdc) { RECT rcEdit; HWND hwnd = ped->hwnd; HBRUSH hBrushRemote = NULL; BOOL fNeedDelete = FALSE; 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.
//
HideCaret(hwnd);
if (IsWindowVisible(hwnd)) { CCDBUFFER db;
//
// Erase the background since we don't do it in the erasebkgnd message.
//
GetClientRect(hwnd, &rcEdit);
hdc = CCBeginDoubleBuffer(hdc, &rcEdit, &db); #ifdef _USE_DRAW_THEME_TEXT_
if (!ped->hTheme) #endif // _USE_DRAW_THEME_TEXT_
{ hBrushRemote = Edit_GetBrush(ped, hdc, &fNeedDelete); if (hBrushRemote) { FillRect(hdc, &rcEdit, hBrushRemote);
if (fNeedDelete) { DeleteObject(hBrushRemote); } }
if (ped->fFlatBorder) { DrawFrame(hdc, &rcEdit, 1, DF_WINDOWFRAME); }
} #ifdef _USE_DRAW_THEME_TEXT_
else { HRESULT hr; INT iStateId = Edit_GetStateId(ped);
hr = DrawThemeBackground(ped->hTheme, hdc, EP_EDITTEXT, iStateId, &rcEdit, 0); } #endif // _USE_DRAW_THEME_TEXT_
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 Edit_GetDC does.
//
hOldFont = SelectObject(hdc, ped->hFont); } EditSL_DrawText(ped, hdc, 0);
if (ped->hFont != NULL && hOldFont != NULL) { SelectObject(hdc, hOldFont); }
CCEndDoubleBuffer(&db); }
ShowCaret(hwnd); }
//---------------------------------------------------------------------------//
//
// EditSL_SetFocus AorW
//
// Gives the edit control the focus and notifies the parent
// EN_SETFOCUS.
//
VOID EditSL_SetFocus(PED ped) { if (!ped->fFocus) { HDC hdc; UINT cxCaret;
ped->fFocus = TRUE; InvalidateRect(ped->hwnd, NULL, TRUE);
//
// We don't want to muck with the caret since it isn't created.
//
hdc = Edit_GetDC(ped, TRUE);
//
// Show the current selection if necessary.
//
if (!ped->fNoHideSel) { EditSL_DrawText(ped, hdc, 0); }
//
// Create the caret
//
SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0); if (ped->pLpkEditCallout) { ped->pLpkEditCallout->EditCreateCaret ((PED0)ped, hdc, cxCaret, ped->lineHeight, 0); } else { CreateCaret(ped->hwnd, (HBITMAP)NULL, cxCaret, ped->lineHeight); } EditSL_SetCaretPosition(ped, hdc); Edit_ReleaseDC(ped, hdc, TRUE); ShowCaret(ped->hwnd);
//
// check the capslock key
//
if (GET_STYLE(ped) & ES_PASSWORD) { EditSL_CheckCapsLock(ped); }
}
//
// Notify parent we have the focus
//
Edit_NotifyParent(ped, EN_SETFOCUS); }
//---------------------------------------------------------------------------//
//
// EditSL_KillFocus
//
// The edit control loses the focus and notifies the parent via EN_KILLFOCUS.
//
void EditSL_KillFocus(PED ped, HWND newFocusHwnd) { HWND hwnd = ped->hwnd;
if (ped->fFocus) { DestroyCaret(); ped->fFocus = FALSE;
//
// 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
//
#ifdef _USE_DRAW_THEME_TEXT_
if ((!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel)) || ped->hTheme) #else
if ((!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel))) #endif // _USE_DRAW_THEME_TEXT_
{ InvalidateRect(hwnd, NULL, FALSE); }
}
//
// If we aren't a combo box, notify parent that we lost the focus.
//
if (!ped->listboxHwnd) { Edit_NotifyParent(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)
//
// 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); } }
//
// If we're still valid, invalidate to cause a redraw. It's common
// for some controls be destroyed after losing focus.
//
if ( IsWindow(hwnd) ) { InvalidateRect(hwnd, NULL, FALSE); } }
//---------------------------------------------------------------------------//
//
// EditSL_Paste()
//
// Does actual text paste and update.
//
VOID EditSL_Paste(PED ped) { HDC hdc;
//
// Insert contents of clipboard, after unhilighting current selection
// and deleting it.
//
Edit_DeleteText(ped); EditSL_PasteText(ped);
//
// Update display
//
Edit_NotifyParent(ped, EN_UPDATE);
hdc = Edit_GetDC(ped,FALSE);
EditSL_ScrollText(ped, hdc); EditSL_DrawText(ped, hdc, 0);
Edit_ReleaseDC(ped,hdc,FALSE);
//
// Tell parent our text contents changed.
//
Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); }
//---------------------------------------------------------------------------//
//
// EditSL_Create
//
// Creates the edit control for the window hwnd by allocating memory
// as required from the application's heap. Notifies parent if no memory
// error (after cleaning up if needed). Returns TRUE if no error else return s
// -1.
//
LONG EditSL_Create(PED ped, LPCREATESTRUCT lpCreateStruct) { LPSTR lpWindowText; LONG windowStyle = GET_STYLE(ped);
//
// Do the standard creation stuff
//
if (!Edit_Create(ped, windowStyle)) { return -1; }
//
// Single lines always have no undo and 1 line
//
ped->cLines = 1; ped->undoType = UNDO_NONE;
//
// Check if this edit control is part of a combobox and get a pointer to the
// combobox structure.
//
if (windowStyle & ES_COMBOBOX) { ped->listboxHwnd = GetDlgItem(lpCreateStruct->hwndParent, CBLISTBOXID); }
//
// Set the default font to be the system font.
//
if ( !Edit_SetFont(ped, NULL, FALSE) ) {
// If setting the font fails, our textmetrics can potentially be left
// unitialize. Fail to create the control.
return -1; }
//
// Set the window text if needed. Return false if we can't set the text
// SLSetText notifies the parent in case there is a no memory error.
//
lpWindowText = (LPSTR)lpCreateStruct->lpszName;
if ((lpWindowText != NULL) && !IsEmptyString(lpWindowText, ped->fAnsi) && !Edit_SetEditText(ped, lpWindowText)) { return -1; }
if (windowStyle & ES_PASSWORD) { LOGFONT lfFont = {0}; LoadString(HINST_THISDLL, IDS_PASSWORDCHARFONT, lfFont.lfFaceName, ARRAYSIZE(lfFont.lfFaceName)); lfFont.lfWeight = FW_NORMAL; lfFont.lfCharSet = DEFAULT_CHARSET;
ped->hFontPassword = CreateFontIndirect(&lfFont); if (ped->hFontPassword && Edit_SetFont(ped, ped->hFontPassword, FALSE)) { WCHAR szChar[10]; UINT uChar;
LoadString(HINST_THISDLL, IDS_PASSWORDCHAR, szChar, ARRAYSIZE(szChar)); uChar = StrToInt(szChar); Edit_SetPasswordCharHandler(ped, uChar); } else { Edit_SetPasswordCharHandler(ped, (UINT)'*'); } }
//
// Since memory cleared to 0 when allocated, this should still be NULL
//
ASSERT(ped->pszCueBannerText == NULL);
return TRUE; }
//---------------------------------------------------------------------------//
//
// EditSL_Undo AorW
//
// Handles UNDO for single line edit controls.
//
BOOL EditSL_Undo(PED ped) { PBYTE hDeletedText = ped->hDeletedText; BOOL fDelete = (BOOL)(ped->undoType & UNDO_DELETE); ICH cchDeleted = ped->cchDeleted; ICH ichDeleted = ped->ichDeleted; BOOL fUpdate = FALSE;
if (ped->undoType == UNDO_NONE) { //
// No undo...
//
return FALSE; }
ped->hDeletedText = NULL; ped->cchDeleted = 0; ped->ichDeleted = (ICH)-1; ped->undoType &= ~UNDO_DELETE;
if (ped->undoType == UNDO_INSERT) { ped->undoType = UNDO_NONE;
//
// Set the selection to the inserted text
//
EditSL_SetSelection(ped, ped->ichInsStart, ped->ichInsEnd); ped->ichInsStart = ped->ichInsEnd = (ICH)-1;
//
// Delete the selected text and save it in undo buff.
// Call Edit_DeleteText() instead of sending a VK_BACK message
// which results in an EN_UPDATE notification send even before
// we insert the deleted chars. This results in Bug #6610.
// Fix for Bug #6610 -- SANKAR -- 04/19/91 --
//
if (Edit_DeleteText(ped)) { //
// Text was deleted -- flag for update and clear selection
//
fUpdate = TRUE; EditSL_SetSelection(ped, ichDeleted, ichDeleted); } }
if (fDelete) { HWND hwndSave = ped->hwnd; // Used for validation.
//
// Insert deleted chars. Set the selection to the inserted text.
//
EditSL_SetSelection(ped, ichDeleted, ichDeleted); EditSL_InsertText(ped, hDeletedText, cchDeleted); GlobalFree(hDeletedText);
if (!IsWindow(hwndSave)) { return FALSE; }
EditSL_SetSelection(ped, ichDeleted, ichDeleted + cchDeleted); fUpdate = TRUE; }
if (fUpdate) { //
// If we have something to update, send EN_UPDATE before and
// EN_CHANGE after the actual update.
// A part of the fix for Bug #6610 -- SANKAR -- 04/19/91 --
//
Edit_NotifyParent(ped, EN_UPDATE);
if (IsWindowVisible(ped->hwnd)) { Edit_InvalidateClient(ped, FALSE); }
Edit_NotifyParent(ped, EN_CHANGE);
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); }
return TRUE; }
//---------------------------------------------------------------------------//
//
// EditSL_SetCueBanner (Unicode Only)
//
// Handles setting the cue banner text for an edit control.
//
BOOL EditSL_SetCueBanner(PED ped, LPCWSTR pszBanner) { BOOL retVal = FALSE;
if (pszBanner != NULL) { //
// Store the input string into the ped's pointer. Str_SetPtr will
// allocate/free memory as needed.
//
retVal = Str_SetPtr(&(ped->pszCueBannerText), pszBanner);
//
// Redraw the control
//
InvalidateRect(ped->hwnd, NULL, FALSE); }
return retVal; }
//---------------------------------------------------------------------------//
//
// EditSL_WndProc
//
// Class procedure for all single line edit controls.
// Dispatches all messages to the appropriate handlers which are named
// as follows:
// EditSL_ (single line) prefixes all single line edit control procedures while
// Edit_ (edit control) prefixes all common handlers.
//
// The EditSL_WndProc only handles messages specific to single line edit
// controls.
//
LRESULT EditSL_WndProc(PED ped, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; POINT pt;
switch (message) { case WM_INPUTLANGCHANGE: if (ped && ped->fFocus && ped->pLpkEditCallout) { UINT cxCaret;
SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0); HideCaret(ped->hwnd); hdc = Edit_GetDC(ped, TRUE); DestroyCaret(); ped->pLpkEditCallout->EditCreateCaret ((PED0)ped, hdc, cxCaret, ped->lineHeight, (UINT)lParam); EditSL_SetCaretPosition(ped, hdc); Edit_ReleaseDC(ped, hdc, TRUE); ShowCaret(ped->hwnd); } goto PassToDefaultWindowProc;
case WM_STYLECHANGED: if (ped && ped->pLpkEditCallout) { switch (wParam) { case GWL_STYLE: Edit_UpdateFormat(ped, ((LPSTYLESTRUCT)lParam)->styleNew, GET_EXSTYLE(ped)); return 1L;
case GWL_EXSTYLE: Edit_UpdateFormat(ped, GET_STYLE(ped), ((LPSTYLESTRUCT)lParam)->styleNew); return 1L; } }
goto PassToDefaultWindowProc;
case WM_CHAR:
//
// wParam - the value of the key
// lParam - modifiers, repeat count etc (not used)
//
if (!ped->fEatNextChar) { EditSL_Char(ped, (UINT)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 1;
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) && (GetWindowStyle(ped->hwndParent) & CBS_DROPDOWN) && SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L))) { code |= DLGC_WANTMESSAGE; } break; } }
return code; }
break;
case WM_KEYDOWN:
//
// wParam - virt keycode of the given key
// lParam - modifiers such as repeat count etc. (not used)
//
EditSL_KeyDown(ped, (UINT)wParam, 0);
break;
case WM_KILLFOCUS:
//
// wParam - handle of the window that receives the input focus
// lParam - not used
//
EditSL_KillFocus(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
//
POINTSTOPOINT(pt, lParam); EditSL_MouseMotion(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 EditSL_Create(ped, (LPCREATESTRUCT)lParam);
break;
case WM_PRINTCLIENT: //
// wParam -- can be hdc from subclassed paint
// lParam -- unused
//
EditSL_Paint(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
//
HideCaret(ped->hwnd); hdc = BeginPaint(ped->hwnd, &ps); }
//
// PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are
// slightly different.
//
if (IsWindowVisible(ped->hwnd)) { EditSL_Paint(ped, hdc); }
if (!wParam) { EndPaint(ped->hwnd, &ps); ShowCaret(ped->hwnd); }
break;
case WM_PASTE:
//
// wParam - not used
// lParam - not used
//
if (!ped->fReadOnly) { EditSL_Paste(ped); }
break;
case WM_SETFOCUS:
//
// wParam - handle of window that loses the input focus (may be NULL)
// lParam - not used
//
EditSL_SetFocus(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
Edit_Size(ped, NULL, TRUE);
return 0;
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 SkipDropCombo; } }
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 SkipDropCombo; }
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); } }
SkipDropCombo: if (wParam == VK_BACK) { SendMessage(ped->hwnd, WM_UNDO, 0, 0L); break; } else { 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 Edit_GetTextHandler(ped, (*(LPWORD)lParam), (LPSTR)lParam, FALSE);
case EM_LINELENGTH:
//
// wParam - ignored
// lParam - ignored
//
return (LONG)ped->cch;
case EM_SETSEL: //
// wParam -- start pos
// lParam -- end pos
//
EditSL_SetSelection(ped, (ICH)wParam, (ICH)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
//
EditSL_ReplaceSel(ped, (LPSTR)lParam); if (!ped->f40Compat || !wParam) { Edit_EmptyUndo(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;
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 = Edit_GetDC(ped, TRUE);
if (message == EM_POSFROMCHAR) { xyPos = MAKELONG(EditSL_IchToLeftXPos(ped, hdc, (ICH)wParam), 0); } else { POINTSTOPOINT(pt, lParam); xyPos = EditSL_MouseToIch(ped, hdc, &pt); }
Edit_ReleaseDC(ped, hdc, TRUE); return (LRESULT)xyPos; }
case WM_UNDO: case EM_UNDO: EditSL_Undo(ped); break;
case EM_SETCUEBANNER: //
// This message passes in a LPCWSTR as the lParam to set the
// cue banner text.
//
// Call function to set the text:
return (LRESULT)EditSL_SetCueBanner(ped, (LPCWSTR) lParam); break;
default: PassToDefaultWindowProc: return DefWindowProc(ped->hwnd, message, wParam, lParam); break; }
return 1L; }
|