Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

5972 lines
162 KiB

#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "edit.h"
//---------------------------------------------------------------------------//
//
// Number of lines to bump when reallocating index buffer
//
#define LINEBUMP 32
//
// Used for ML scroll updates
//
#define ML_REFRESH 0xffffffff
//---------------------------------------------------------------------------//
//
// Forwards
//
ICH EditML_Line(PED, ICH);
VOID EditML_ShiftchLines(PED, ICH, int);
VOID EditML_Char(PED, DWORD, int);
VOID EditML_MouseMotion(PED, UINT, UINT, LPPOINT);
BOOL EditML_EnsureCaretVisible(PED);
VOID EditML_DrawText(PED, HDC, ICH, ICH, BOOL);
BOOL EditML_InsertCrCrLf(PED);
VOID EditML_StripCrCrLf(PED);
VOID EditML_SetHandle(PED, HANDLE);
LONG EditML_GetLine(PED, ICH, ICH, LPSTR);
ICH EditML_LineIndex(PED, ICH);
ICH EditML_LineLength(PED, ICH);
VOID EditML_SetSelection(PED, BOOL, ICH, ICH);
BOOL EditML_SetTabStops(PED, int, LPINT);
BOOL EditML_Undo(PED);
LONG EditML_Create(PED, LPCREATESTRUCT);
//---------------------------------------------------------------------------//
__inline void EditML_SanityCheck(PED ped)
{
UNREFERENCED_PARAMETER(ped); // For free build
UserAssert(ped->cch >= ped->chLines[ped->cLines - 1]);
}
//---------------------------------------------------------------------------//
//
// EditML_GetLineWidth
//
// Returns the max width in a line. Edit_TabTheTextOut() ensures that max
// width won't overflow.
//
UINT EditML_GetLineWidth(HDC hdc, LPSTR lpstr, int nCnt, PED ped)
{
return Edit_TabTheTextOut(hdc, 0, 0, 0, 0, lpstr, nCnt, 0, ped, 0, ECT_CALC, NULL);
}
//---------------------------------------------------------------------------//
//
// EditML_Size
//
// Handles resizing of the edit control window and updating thereof.
//
// Sets the edit field's formatting area given the passed in "client area".
// We fudge it if it doesn't seem reasonable.
//
VOID EditML_Size(PED ped, BOOL fRedraw)
{
//
// Calculate the # of lines we can fit in our rectangle.
//
ped->ichLinesOnScreen = (ped->rcFmt.bottom - ped->rcFmt.top) / ped->lineHeight;
//
// Make the format rectangle height an integral number of lines
//
ped->rcFmt.bottom = ped->rcFmt.top + ped->ichLinesOnScreen * ped->lineHeight;
//
// Rebuild the line array
//
if (ped->fWrap)
{
EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
EditML_UpdateiCaretLine(ped);
}
else
{
EditML_Scroll(ped, TRUE, ML_REFRESH, 0, fRedraw);
EditML_Scroll(ped, FALSE, ML_REFRESH, 0, fRedraw);
}
}
//---------------------------------------------------------------------------//
//
// EditML_CalcXOffset
//
// Calculates the horizontal offset (indent) required for centered
// and right justified lines.
//
int EditML_CalcXOffset(PED ped, HDC hdc, int lineNumber)
{
PSTR pText;
ICH lineLength;
ICH lineWidth;
if (ped->format == ES_LEFT)
{
return 0;
}
lineLength = EditML_Line(ped, lineNumber);
if (lineLength)
{
pText = Edit_Lock(ped) + ped->chLines[lineNumber] * ped->cbChar;
hdc = Edit_GetDC(ped, TRUE);
lineWidth = EditML_GetLineWidth(hdc, pText, lineLength, ped);
Edit_ReleaseDC(ped, hdc, TRUE);
Edit_Unlock(ped);
}
else
{
lineWidth = 0;
}
//
// If a SPACE or a TAB was eaten at the end of a line by EditML_BuildchLines
// to prevent a delimiter appearing at the begining of a line, the
// the following calculation will become negative causing this bug.
// So, now, we take zero in such cases.
// Fix for Bug #3566 --01/31/91-- SANKAR --
//
lineWidth = max(0, (int)(ped->rcFmt.right-ped->rcFmt.left-lineWidth));
if (ped->format == ES_CENTER)
{
return (lineWidth / 2);
}
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.
//
return max(0, (int)(lineWidth-1));
}
return 0;
}
//---------------------------------------------------------------------------//
//
// Edit_MoveSelection AorW
//
// Moves the selection character in the direction indicated. Assumes
// you are starting at a legal point, we decrement/increment the ich. Then,
// This decrements/increments it some more to get past CRLFs...
//
ICH Edit_MoveSelection(PED ped, ICH ich, BOOL fLeft)
{
if (fLeft && ich > 0)
{
//
// Move left
//
ich = Edit_PrevIch( ped, NULL, ich );
if (ich)
{
if (ped->fAnsi)
{
LPSTR pText;
//
// Check for CRLF or CRCRLF
//
pText = Edit_Lock(ped) + ich;
//
// Move before CRLF or CRCRLF
//
if (*(WORD UNALIGNED *)(pText - 1) == 0x0A0D)
{
ich--;
if (ich && *(pText - 2) == 0x0D)
ich--;
}
Edit_Unlock(ped);
}
else
{
LPWSTR pwText;
//
// Check for CRLF or CRCRLF
//
pwText = (LPWSTR)Edit_Lock(ped) + ich;
//
// Move before CRLF or CRCRLF
//
if (*(pwText - 1) == 0x0D && *pwText == 0x0A)
{
ich--;
if (ich && *(pwText - 2) == 0x0D)
ich--;
}
Edit_Unlock(ped);
}
}
}
else if (!fLeft && ich < ped->cch)
{
//
// Move right.
//
ich = Edit_NextIch( ped, NULL, ich );
if (ich < ped->cch)
{
if (ped->fAnsi)
{
LPSTR pText;
pText = Edit_Lock(ped) + ich;
//
// Move after CRLF
//
if (*(WORD UNALIGNED *)(pText - 1) == 0x0A0D)
{
ich++;
}
else
{
//
// Check for CRCRLF
//
if (ich && *(WORD UNALIGNED *)pText == 0x0A0D && *(pText - 1) == 0x0D)
{
ich += 2;
}
}
Edit_Unlock(ped);
}
else
{
LPWSTR pwText;
pwText = (LPWSTR)Edit_Lock(ped) + ich;
//
// Move after CRLF
//
if (*(pwText - 1) == 0x0D && *pwText == 0x0A)
{
ich++;
}
else
{
//
// Check for CRCRLF
//
if (ich && *(pwText - 1) == 0x0D && *pwText == 0x0D &&
*(pwText + 1) == 0x0A)
{
ich += 2;
}
}
Edit_Unlock(ped);
}
}
}
return ich;
}
//---------------------------------------------------------------------------//
//
// Edit_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 Edit_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;
}
//---------------------------------------------------------------------------//
//
// EditML_SetCaretPosition AorW
//
// If the window has the focus, find where the caret belongs and move
// it there.
//
VOID EditML_SetCaretPosition(PED ped, HDC hdc)
{
POINT position;
BOOL prevLine;
int x = -20000;
int y = -20000;
//
// 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 || !IsWindowVisible(ped->hwnd))
{
return;
}
//
// Find the position of the caret
//
if (!ped->fCaretHidden &&
((ICH) ped->iCaretLine >= ped->ichScreenStart) &&
((ICH) ped->iCaretLine < (ped->ichScreenStart + ped->ichLinesOnScreen)))
{
RECT rcRealFmt;
if (ped->f40Compat)
{
GetClientRect(ped->hwnd, &rcRealFmt);
IntersectRect(&rcRealFmt, &rcRealFmt, &ped->rcFmt);
}
else
{
CopyRect(&rcRealFmt, &ped->rcFmt);
}
if (ped->cLines - 1 != ped->iCaretLine && ped->ichCaret == ped->chLines[ped->iCaretLine + 1])
{
prevLine = TRUE;
}
else
{
prevLine = FALSE;
}
EditML_IchToXYPos(ped, hdc, ped->ichCaret, prevLine, &position);
if ( (position.y >= rcRealFmt.top) &&
(position.y <= rcRealFmt.bottom - ped->lineHeight))
{
int xPos = position.x;
int cxCaret;
SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0);
if (ped->fWrap ||
((xPos > (rcRealFmt.left - cxCaret)) &&
(xPos <= rcRealFmt.right)))
{
//
// Make sure the caret is in the visible region if word
// wrapping. This is so that the caret will be visible if the
// line ends with a space.
//
x = max(xPos, rcRealFmt.left);
x = min(x, rcRealFmt.right - cxCaret);
y = position.y;
}
}
}
if (ped->pLpkEditCallout)
{
SetCaretPos(x + ped->iCaretOffset, y);
}
else
{
SetCaretPos(x, y);
}
//
// FE_IME : EditML_SetCaretPosition -- ImmSetCompositionWindow(CFS_RECT)
//
if (g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0)))
{
if (x != -20000 && y != -20000)
{
Edit_ImmSetCompositionWindow(ped, x, y);
}
}
}
//---------------------------------------------------------------------------//
//
// EditML_Line
//
// Returns the length of the line (cch) given by lineNumber ignoring any
// CRLFs in the line.
//
ICH EditML_Line(PED ped, ICH lineNumber)
{
ICH result;
if (lineNumber >= ped->cLines)
{
return 0;
}
if (lineNumber == ped->cLines - 1)
{
//
// Since we can't have a CRLF on the last line
//
return (ped->cch - ped->chLines[ped->cLines - 1]);
}
else
{
result = ped->chLines[lineNumber + 1] - ped->chLines[lineNumber];
TraceMsg(TF_STANDARD, "Edit: MLLine result=%d", result);
//
// Now check for CRLF or CRCRLF at end of line
//
if (result > 1)
{
if (ped->fAnsi)
{
LPSTR pText;
pText = Edit_Lock(ped) + ped->chLines[lineNumber + 1] - 2;
if (*(WORD UNALIGNED *)pText == 0x0A0D)
{
result -= 2;
if (result && *(--pText) == 0x0D)
{
//
// In case there was a CRCRLF
//
result--;
}
}
}
else
{
LPWSTR pwText;
pwText = (LPWSTR)Edit_Lock(ped) + (ped->chLines[lineNumber + 1] - 2);
if (*(DWORD UNALIGNED *)pwText == 0x000A000D)
{
result = result - 2;
if (result && *(--pwText) == 0x0D)
{
//
// In case there was a CRCRLF
//
result--;
}
}
}
Edit_Unlock(ped);
}
}
return result;
}
//---------------------------------------------------------------------------//
//
// EditML_IchToLine AorW
//
// Returns the line number (starting from 0) which contains the given
// character index. If ich is -1, return the line the first char in the
// selection is on (the caret if no selection)
//
INT EditML_IchToLine(PED ped, ICH ich)
{
int iLo, iHi, iLine;
iLo = 0;
iHi = ped->cLines;
if (ich == (ICH)-1)
{
ich = ped->ichMinSel;
}
while (iLo < iHi - 1)
{
iLine = max((iHi - iLo)/2, 1) + iLo;
if (ped->chLines[iLine] > ich)
{
iHi = iLine;
}
else
{
iLo = iLine;
}
}
return iLo;
}
//---------------------------------------------------------------------------//
//
// EditML_IchToYPos
//
// Given an ich, return its y coordinate with respect to the top line
// displayed in the window. If prevLine is TRUE and if the ich is at the
// beginning of the line, return the y coordinate of the
// previous line (if it is not a CRLF).
//
// Added for the LPK (3Dec96) - with an LPK installed, calculating X position is
// a far more processor intensive job. Where only the Y position is required
// this routine should be called instead of EditML_IchToXYPos.
//
// Called only when LPK installed.
//
INT EditML_IchToYPos( PED ped, ICH ich, BOOL prevLine)
{
int iline;
int yPosition;
PSTR pText;
//
// Determine what line the character is on
//
iline = EditML_IchToLine(ped, ich);
//
// Calc. the yPosition now. Note that this may change by the height of one
// char if the prevLine flag is set and the ICH is at the beginning of a line.
//
yPosition = (iline - ped->ichScreenStart) * ped->lineHeight + ped->rcFmt.top;
pText = Edit_Lock(ped);
if (prevLine && iline && (ich == ped->chLines[iline]) &&
(!AWCOMPARECHAR(ped, pText + (ich - 2) * ped->cbChar, 0x0D) ||
!AWCOMPARECHAR(ped, pText + (ich - 1) * ped->cbChar, 0x0A)))
{
//
// First char in the line. We want Y position of the previous
// line if we aren't at the 0th line.
//
iline--;
yPosition = yPosition - ped->lineHeight;
}
Edit_Unlock(ped);
return yPosition;
}
//---------------------------------------------------------------------------//
//
// EditML_IchToXYPos
//
// Given an ich, return its x,y coordinates with respect to the top
// left character displayed in the window. Returns the coordinates of the top
// left position of the char. If prevLine is TRUE then if the ich is at the
// beginning of the line, we will return the coordinates to the right of the
// last char on the previous line (if it is not a CRLF).
//
VOID EditML_IchToXYPos(PED ped, HDC hdc, ICH ich, BOOL prevLine, LPPOINT ppt)
{
int iline;
ICH cch;
int xPosition, yPosition;
int xOffset;
//
// For horizontal scroll displacement on left justified text and
// for indent on centered or right justified text
//
PSTR pText, pTextStart, pLineStart;
//
// Determine what line the character is on
//
iline = EditML_IchToLine(ped, ich);
//
// Calc. the yPosition now. Note that this may change by the height of one
// char if the prevLine flag is set and the ICH is at the beginning of a line.
//
yPosition = (iline - ped->ichScreenStart) * ped->lineHeight + ped->rcFmt.top;
//
// Now determine the xPosition of the character
//
pTextStart = Edit_Lock(ped);
if (prevLine && iline && (ich == ped->chLines[iline]) &&
(!AWCOMPARECHAR(ped, pTextStart + (ich - 2) * ped->cbChar, 0x0D) ||
!AWCOMPARECHAR(ped, pTextStart + (ich - 1) * ped->cbChar, 0x0A)))
{
//
// First char in the line. We want text extent upto end of the previous
// line if we aren't at the 0th line.
//
iline--;
yPosition = yPosition - ped->lineHeight;
pLineStart = pTextStart + ped->chLines[iline] * ped->cbChar;
//
// Note that we are taking the position in front of any CRLFs in the
// text.
//
cch = EditML_Line(ped, iline);
}
else
{
pLineStart = pTextStart + ped->chLines[iline] * ped->cbChar;
pText = pTextStart + ich * ped->cbChar;
//
// Strip off CRLF or CRCRLF. Note that we may be pointing to a CR but in
// which case we just want to strip off a single CR or 2 CRs.
//
//
// We want pText to point to the first CR at the end of the line if
// there is one. Thus, we will get an xPosition to the right of the last
// visible char on the line otherwise we will be to the left of
// character ich.
//
//
// Check if we at the end of text
//
if (ich < ped->cch)
{
if (ped->fAnsi)
{
if (ich && *(WORD UNALIGNED *)(pText - 1) == 0x0A0D)
{
pText--;
if (ich > 2 && *(pText - 1) == 0x0D)
{
pText--;
}
}
}
else
{
LPWSTR pwText = (LPWSTR)pText;
if (ich && *(DWORD UNALIGNED *)(pwText - 1) == 0x000A000D)
{
pwText--;
if (ich > 2 && *(pwText - 1) == 0x0D)
{
pwText--;
}
}
pText = (LPSTR)pwText;
}
}
if (pText < pLineStart)
{
pText = pLineStart;
}
cch = (ICH)(pText - pLineStart)/ped->cbChar;
}
//
// Find out how many pixels we indent the line for funny formats
//
if (ped->pLpkEditCallout)
{
//
// Must find position at start of character offset cch from start of line.
// This depends on the layout and the reading order
//
xPosition = ped->pLpkEditCallout->EditIchToXY(
(PED0)ped, hdc, pLineStart, EditML_Line(ped, iline), cch);
}
else
{
if (ped->format != ES_LEFT)
{
xOffset = EditML_CalcXOffset(ped, hdc, iline);
}
else
{
xOffset = -(int)ped->xOffset;
}
xPosition = ped->rcFmt.left + xOffset +
EditML_GetLineWidth(hdc, pLineStart, cch, ped);
}
Edit_Unlock(ped);
ppt->x = xPosition;
ppt->y = yPosition;
return;
}
//---------------------------------------------------------------------------//
//
// EditML_MouseToIch AorW
//
// Returns the closest cch to where the mouse point is. Also optionally
// returns lineindex in pline (So that we can tell if we are at the beginning
// of the line or end of the previous line.)
//
ICH EditML_MouseToIch(PED ped, HDC hdc, LPPOINT mousePt, LPICH pline)
{
int xOffset;
LPSTR pLineStart;
int height = mousePt->y;
int line; //WASINT
int width = mousePt->x;
ICH cch;
ICH cLineLength;
ICH cLineLengthNew;
ICH cLineLengthHigh;
ICH cLineLengthLow;
ICH cLineLengthTemp;
int textWidth;
int iCurWidth;
int lastHighWidth, lastLowWidth;
//
// First determine which line the mouse is pointing to.
//
line = ped->ichScreenStart;
if (height <= ped->rcFmt.top)
{
//
// Either return 0 (the very first line, or one line before the top line
// on the screen. Note that these are signed mins and maxes since we
// don't expect (or allow) more than 32K lines.
//
line = max(0, line-1);
}
else if (height >= ped->rcFmt.bottom)
{
//
// Are we below the last line displayed
//
line = min(line+(int)ped->ichLinesOnScreen, (int)(ped->cLines-1));
}
else
{
//
// We are somewhere on a line visible on screen
//
line = min(line + (int)((height - ped->rcFmt.top) / ped->lineHeight),
(int)(ped->cLines - 1));
}
//
// Now determine what horizontal character the mouse is pointing to.
//
pLineStart = Edit_Lock(ped) + ped->chLines[line] * ped->cbChar;
cLineLength = EditML_Line(ped, line); // Length is sans CRLF or CRCRLF
TraceMsg(TF_STANDARD, "Edit: EditML_Line(ped=%x, line=%d) returned %d", ped, line, cLineLength);
UserAssert((int)cLineLength >= 0);
//
// If the language pack is loaded, visual and logical character order
// may differ.
//
if (ped->pLpkEditCallout)
{
//
// Use the language pack to find the character nearest the cursor.
//
cch = ped->chLines[line] + ped->pLpkEditCallout->EditMouseToIch
((PED0)ped, hdc, pLineStart, cLineLength, width);
}
else
{
//
// xOffset will be a negative value for center and right justified lines.
// ie. We will just displace the lines left by the amount of indent for
// right and center justification. Note that ped->xOffset will be 0 for
// these lines since we don't support horizontal scrolling with them.
//
if (ped->format != ES_LEFT)
{
xOffset = EditML_CalcXOffset(ped, hdc, line);
}
else
{
//
// So that we handle a horizontally scrolled window for left justified
// text.
//
xOffset = 0;
}
width = width - xOffset;
//
// The code below is tricky... I depend on the fact that ped->xOffset is 0
// for right and center justified lines
//
//
// Now find out how many chars fit in the given width
//
if (width >= ped->rcFmt.right)
{
//
// Return 1+last char in line or one plus the last char visible
//
cch = Edit_CchInWidth(ped, hdc, pLineStart, cLineLength,
ped->rcFmt.right - ped->rcFmt.left + ped->xOffset, TRUE);
//
// Consider DBCS in case of width >= ped->rcFmt.right
//
// Since Edit_CchInWidth and EditML_LineLength takes care of DBCS, we only need to
// worry about if the last character is a double byte character or not.
//
// cch = ped->chLines[line] + min( Edit_NextIch(ped, pLineStart, cch), cLineLength);
//
// we need to adjust the position. LiZ -- 5/5/93
//
if (ped->fAnsi && ped->fDBCS)
{
ICH cch2 = min(cch+1,cLineLength);
if (Edit_AdjustIch(ped, pLineStart, cch2) != cch2)
{
//
// Displayed character on the right edge is DBCS
//
cch = min(cch+2,cLineLength);
}
else
{
cch = cch2;
}
cch += ped->chLines[line];
}
else
{
cch = ped->chLines[line] + min(cch + 1, cLineLength);
}
}
else if (width <= ped->rcFmt.left + ped->aveCharWidth / 2)
{
//
// Return first char in line or one minus first char visible. Note that
// ped->xOffset is 0 for right and centered text so we will just return
// the first char in the string for them. (Allow a avecharwidth/2
// positioning border so that the user can be a little off...
//
cch = Edit_CchInWidth(ped, hdc, pLineStart, cLineLength, ped->xOffset, TRUE);
if (cch)
{
cch--;
}
cch = Edit_AdjustIch( ped, pLineStart, cch );
cch += ped->chLines[line];
}
else
{
if (cLineLength == 0)
{
cch = ped->chLines[line];
goto edUnlock;
}
iCurWidth = width + ped->xOffset - ped->rcFmt.left;
//
// If the user clicked past the end of the text, return the last character
//
lastHighWidth = EditML_GetLineWidth(hdc, pLineStart, cLineLength, ped);
if (lastHighWidth <= iCurWidth)
{
cLineLengthNew = cLineLength;
goto edAdjust;
}
//
// Now the mouse is somewhere on the visible portion of the text
// remember cch contains the length of the line.
//
cLineLengthLow = 0;
cLineLengthHigh = cLineLength + 1;
lastLowWidth = 0;
while (cLineLengthLow < cLineLengthHigh - 1)
{
cLineLengthNew = (cLineLengthHigh + cLineLengthLow) / 2;
if (ped->fAnsi && ped->fDBCS)
{
//
// EditML_GetLineWidth returns meaningless value for truncated DBCS.
//
cLineLengthTemp = Edit_AdjustIch(ped, pLineStart, cLineLengthNew);
textWidth = EditML_GetLineWidth(hdc, pLineStart, cLineLengthTemp, ped);
}
else
{
textWidth = EditML_GetLineWidth(hdc, pLineStart, cLineLengthNew, ped);
}
if (textWidth > iCurWidth)
{
cLineLengthHigh = cLineLengthNew;
lastHighWidth = textWidth;
}
else
{
cLineLengthLow = cLineLengthNew;
lastLowWidth = textWidth;
}
}
//
// When the while ends, you can't know the exact desired position.
// Try to see if the mouse pointer was on the farest half
// of the char we got and if so, adjust cch.
//
if (cLineLengthLow == cLineLengthNew)
{
//
// Need to compare with lastHighWidth
//
if ((lastHighWidth - iCurWidth) < (iCurWidth - textWidth))
{
cLineLengthNew++;
}
}
else
{
//
// Need to compare with lastLowHigh
//
if ((iCurWidth - lastLowWidth) < (textWidth - iCurWidth))
{
cLineLengthNew--;
}
}
edAdjust:
cLineLength = Edit_AdjustIch( ped, pLineStart, cLineLengthNew );
cch = ped->chLines[line] + cLineLength;
}
}
edUnlock:
Edit_Unlock(ped);
if (pline)
{
*pline = line;
}
return cch;
}
//---------------------------------------------------------------------------//
//
// EditML_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 EditML_ChangeSelection(PED ped, HDC hdc, ICH ichNewMinSel, ICH ichNewMaxSel)
{
ICH temp;
ICH ichOldMinSel, ichOldMaxSel;
if (ichNewMinSel > ichNewMaxSel)
{
temp = ichNewMinSel;
ichNewMinSel = ichNewMaxSel;
ichNewMaxSel = temp;
}
ichNewMinSel = min(ichNewMinSel, ped->cch);
ichNewMaxSel = min(ichNewMaxSel, ped->cch);
//
// Save the current selection
//
ichOldMinSel = ped->ichMinSel;
ichOldMaxSel = ped->ichMaxSel;
//
// Set new selection
//
ped->ichMinSel = ichNewMinSel;
ped->ichMaxSel = ichNewMaxSel;
//
// This finds the XOR of the old and new selection regions and redraws it.
// There is nothing to repaint if we aren't visible or our selection
// is hidden.
//
if (IsWindowVisible(ped->hwnd) && (ped->fFocus || ped->fNoHideSel))
{
SELBLOCK Blk[2];
int i;
if (ped->fFocus)
{
HideCaret(ped->hwnd);
}
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 both Blk[0] and Blk[1], if they exist
//
for (i = 0; i < 2; i++)
{
if (Blk[i].StPos != 0xFFFFFFFF)
EditML_DrawText(ped, hdc, Blk[i].StPos, Blk[i].EndPos, TRUE);
}
}
//
// Update caret.
//
EditML_SetCaretPosition(ped, hdc);
if (ped->fFocus)
{
ShowCaret(ped->hwnd);
}
}
}
//---------------------------------------------------------------------------//
//
// EditML_UpdateiCaretLine AorW
//
// This updates the ped->iCaretLine field from the ped->ichCaret;
// Also, when the caret gets to the beginning of next line, pop it up to
// the end of current line when inserting text;
//
VOID EditML_UpdateiCaretLine(PED ped)
{
PSTR pText;
ped->iCaretLine = EditML_IchToLine(ped, ped->ichCaret);
//
// If caret gets to beginning of next line, pop it up to end of current line
// when inserting text.
//
pText = Edit_Lock(ped) +
(ped->ichCaret - 1) * ped->cbChar;
if (ped->iCaretLine && ped->chLines[ped->iCaretLine] == ped->ichCaret &&
(!AWCOMPARECHAR(ped, pText - ped->cbChar, 0x0D) ||
!AWCOMPARECHAR(ped, pText, 0x0A)))
{
ped->iCaretLine--;
}
Edit_Unlock(ped);
}
//---------------------------------------------------------------------------//
//
// EditML_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. 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.
// do some stuff faster since we will be getting only one or two chars of input.
//
ICH EditML_InsertText(PED ped, LPSTR lpText, ICH cchInsert, BOOL fUserTyping)
{
HDC hdc;
ICH validCch = cchInsert;
ICH oldCaret = ped->ichCaret;
int oldCaretLine = ped->iCaretLine;
BOOL fCRLF = FALSE;
LONG ll, hl;
POINT xyPosInitial;
POINT xyPosFinal;
HWND hwndSave = ped->hwnd;
UNDO undo;
ICH validCchTemp;
xyPosInitial.x=0;
xyPosInitial.y=0;
xyPosFinal.x=0;
xyPosFinal.y=0;
if (validCch == 0)
{
return 0;
}
if (ped->cchTextMax <= ped->cch)
{
//
// When the max chars is reached already, notify parent
// Fix for Bug #4183 -- 02/06/91 -- SANKAR --
//
Edit_NotifyParent(ped,EN_MAXTEXT);
return 0;
}
//
// Limit the amount of text we add
//
validCch = min(validCch, ped->cchTextMax - ped->cch);
//
// Make sure we don't split a CRLF in half
//
if (validCch)
{
if (ped->fAnsi)
{
if (*(WORD UNALIGNED *)(lpText + validCch - 1) == 0x0A0D)
{
validCch--;
}
}
else
{
if (*(DWORD UNALIGNED *)(lpText + (validCch - 1) * ped->cbChar) == 0x000A000D)
{
validCch--;
}
}
}
if (!validCch)
{
//
// When the max chars is reached already, notify parent
// Fix for Bug #4183 -- 02/06/91 -- SANKAR --
//
Edit_NotifyParent(ped,EN_MAXTEXT);
return 0;
}
if (validCch == 2)
{
if (ped->fAnsi)
{
if (*(WORD UNALIGNED *)lpText == 0x0A0D)
{
fCRLF = TRUE;
}
}
else
{
if (*(DWORD UNALIGNED *)lpText == 0x000A000D)
{
fCRLF = TRUE;
}
}
}
//
// Save current undo state always, but clear it out only if !AutoVScroll
//
Edit_SaveUndo(Pundo(ped), (PUNDO)&undo, !ped->fAutoVScroll);
hdc = Edit_GetDC(ped, FALSE);
//
// We only need the y position. Since with an LPK loaded
// calculating the x position is an intensive job, just
// call EditML_IchToYPos.
//
if (ped->cch)
{
if (ped->pLpkEditCallout)
{
xyPosInitial.y = EditML_IchToYPos(ped, ped->cch-1, FALSE);
}
else
{
EditML_IchToXYPos(ped, hdc, ped->cch - 1, FALSE, &xyPosInitial);
}
}
//
// Insert the text
//
validCchTemp = validCch; // may not be needed, but just for precautions..
if (!Edit_InsertText(ped, lpText, &validCchTemp))
{
//
// Restore previous undo buffer if it was cleared
//
if (!ped->fAutoVScroll)
{
Edit_SaveUndo((PUNDO)&undo, Pundo(ped), FALSE);
}
Edit_ReleaseDC(ped, hdc, FALSE);
Edit_NotifyParent(ped, EN_ERRSPACE);
return 0;
}
#if DBG
if (validCch != validCchTemp)
{
//
// All characters in lpText has not been inserted to ped.
// This could happen when cch is close to cchMax.
// Better revisit this after NT5 ships.
//
TraceMsg(TF_STANDARD, "Edit: EditML_InsertText: validCch is changed (%x -> %x) in Edit_InsertText.",
validCch, validCchTemp);
}
#endif
//
// Note that ped->ichCaret is updated by Edit_InsertText
//
EditML_BuildchLines(ped, (ICH)oldCaretLine, (int)validCch, fCRLF?(BOOL)FALSE:fUserTyping, &ll, &hl);
if (ped->cch)
{
//
// We only need the y position. Since with an LPK loaded
// calculating the x position is an intensive job, just
// call EditML_IchToYPos.
if (ped->pLpkEditCallout)
{
xyPosFinal.y = EditML_IchToYPos(ped, ped->cch-1, FALSE);
}
else
{
EditML_IchToXYPos(ped, hdc, ped->cch - 1, FALSE,&xyPosFinal);
}
}
if (xyPosFinal.y < xyPosInitial.y && ((ICH)ped->ichScreenStart) + ped->ichLinesOnScreen >= ped->cLines - 1)
{
RECT rc;
CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt);
rc.top = xyPosFinal.y + ped->lineHeight;
if (ped->pLpkEditCallout)
{
int xFarOffset = ped->xOffset + ped->rcFmt.right - ped->rcFmt.left;
//
// Include left or right margins in display unless clipped
// by horizontal scrolling.
//
if (ped->wLeftMargin)
{
if (!(ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
&& ( (!ped->fRtoLReading && ped->xOffset > 0) // LTR and first char not fully in view
|| ( ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) //RTL and last char not fully in view
{
rc.left -= ped->wLeftMargin;
}
}
//
// Process right margin
//
if (ped->wRightMargin)
{
if (!(ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
&& (( ped->fRtoLReading && ped->xOffset > 0) // RTL and first char not fully in view
|| (!ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) // LTR and last char not fully in view
{
rc.right += ped->wRightMargin;
}
}
}
InvalidateRect(ped->hwnd, (LPRECT)&rc, TRUE);
}
if (!ped->fAutoVScroll)
{
if (ped->ichLinesOnScreen < ped->cLines)
{
EditML_Undo(ped);
Edit_EmptyUndo(Pundo(ped));
Edit_SaveUndo(&undo, Pundo(ped), FALSE);
MessageBeep(0);
Edit_ReleaseDC(ped, hdc, FALSE);
//
// When the max lines is reached already, notify parent
// Fix for Bug #7586 -- 10/14/91 -- SANKAR --
//
Edit_NotifyParent(ped,EN_MAXTEXT);
return 0;
}
else
{
Edit_EmptyUndo(&undo);
}
}
if (fUserTyping && ped->fWrap)
{
//
// To avoid oldCaret points intermediate of DBCS character,
// adjust oldCaret position if necessary.
//
// !!!CR If EditML_BuildchLines() returns reasonable value ( and I think
// it does), we don't probably need this. Check this out later.
//
if (ped->fDBCS && ped->fAnsi)
{
oldCaret = Edit_AdjustIch(ped,
Edit_Lock(ped),
min((ICH)LOWORD(ll),oldCaret));
}
else
{
oldCaret = min((ICH)LOWORD(ll), oldCaret);
}
}
//
// Update ped->iCaretLine properly.
//
EditML_UpdateiCaretLine(ped);
Edit_NotifyParent(ped, EN_UPDATE);
//
// Make sure window still exists.
//
if (!IsWindow(hwndSave))
{
return 0;
}
if (IsWindowVisible(ped->hwnd))
{
//
// If the current font has negative A widths, we may have to start
// drawing a few characters before the oldCaret position.
//
if (ped->wMaxNegAcharPos)
{
int iLine = EditML_IchToLine(ped, oldCaret);
oldCaret = max( ((int)(oldCaret - ped->wMaxNegAcharPos)),
((int)(ped->chLines[iLine])));
}
//
// Redraw to end of screen/text if CRLF or large insert
//
if (fCRLF || !fUserTyping)
{
//
// Redraw to end of screen/text if crlf or large insert.
//
EditML_DrawText(ped, hdc, (fUserTyping ? oldCaret : 0), ped->cch, FALSE);
}
else
{
EditML_DrawText(ped, hdc, oldCaret, max(ped->ichCaret, (ICH)hl), FALSE);
}
}
Edit_ReleaseDC(ped, hdc, FALSE);
//
// Make sure we can see the cursor
//
EditML_EnsureCaretVisible(ped);
ped->fDirty = TRUE;
Edit_NotifyParent(ped, EN_CHANGE);
if (validCch < cchInsert)
{
Edit_NotifyParent(ped, EN_MAXTEXT);
}
if (validCch)
{
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
}
//
// Make sure the window still exists.
//
return IsWindow(hwndSave) ? validCch : 0;
}
//---------------------------------------------------------------------------//
//
// EditML_ReplaceSel
//
// Replaces currently selected text with the passed in text, WITH UNDO
// CAPABILITIES.
//
VOID EditML_ReplaceSel(PED ped, LPSTR lpText)
{
ICH cchText;
//
// Delete text, which will put it into the clean undo buffer.
//
Edit_EmptyUndo(Pundo(ped));
EditML_DeleteText(ped);
//
// B#3356
// Some apps do "clear" by selecting all of the text, then replacing
// it with "", in which case EditML_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;
//
// B#1385,1427
// Save undo buffer, but DO NOT CLEAR IT. We want to restore it
// if insertion fails due to OOM.
//
Edit_SaveUndo(Pundo(ped), (PUNDO)&undo, FALSE);
hwndSave = ped->hwnd;
fFailed = (BOOL) !EditML_InsertText(ped, lpText, cchText, FALSE);
if (!IsWindow(hwndSave))
{
return;
}
if (fFailed)
{
//
// UNDO the previous edit
//
Edit_SaveUndo((PUNDO)&undo, Pundo(ped), FALSE);
EditML_Undo(ped);
}
}
}
//---------------------------------------------------------------------------//
//
// EditML_DeleteText AorW
//
// Deletes the characters between ichMin and ichMax. Returns the
// number of characters we deleted.
//
ICH EditML_DeleteText(PED ped)
{
ICH minSel = ped->ichMinSel;
ICH maxSel = ped->ichMaxSel;
ICH cchDelete;
HDC hdc;
int minSelLine;
int maxSelLine;
POINT xyPos;
RECT rc;
BOOL fFastDelete = FALSE;
LONG hl;
INT cchcount = 0;
//
// Get what line the min selection is on so that we can start rebuilding the
// text from there if we delete anything.
//
minSelLine = EditML_IchToLine(ped, minSel);
maxSelLine = EditML_IchToLine(ped, maxSel);
//
// Calculate fFastDelete and cchcount
//
if (ped->fAnsi && ped->fDBCS)
{
if ((ped->fAutoVScroll) &&
(minSelLine == maxSelLine) &&
(ped->chLines[minSelLine] != minSel) &&
(Edit_NextIch(ped,NULL,minSel) == maxSel))
{
fFastDelete = TRUE;
cchcount = ((maxSel - minSel) == 1) ? 0 : -1;
}
}
else if (((maxSel - minSel) == 1) && (minSelLine == maxSelLine) && (ped->chLines[minSelLine] != minSel))
{
fFastDelete = ped->fAutoVScroll ? TRUE : FALSE;
}
cchDelete = Edit_DeleteText(ped);
if (!cchDelete)
{
return 0;
}
//
// Start building lines at minsel line since caretline may be at the max sel
// point.
//
if (fFastDelete)
{
//
// cchcount is (-1) if it's a double byte character
//
EditML_ShiftchLines(ped, minSelLine + 1, -2 + cchcount);
EditML_BuildchLines(ped, minSelLine, 1, TRUE, NULL, &hl);
}
else
{
EditML_BuildchLines(ped, max(minSelLine-1,0), -(int)cchDelete, FALSE, NULL, NULL);
}
EditML_UpdateiCaretLine(ped);
Edit_NotifyParent(ped, EN_UPDATE);
if (IsWindowVisible(ped->hwnd))
{
//
// Now update the screen to reflect the deletion
//
hdc = Edit_GetDC(ped, FALSE);
//
// Otherwise just redraw starting at the line we just entered
//
minSelLine = max(minSelLine-1,0);
EditML_DrawText(ped, hdc, ped->chLines[minSelLine], fFastDelete ? hl : ped->cch, FALSE);
CopyRect(&rc, &ped->rcFmt);
rc.left -= ped->wLeftMargin;
rc.right += ped->wRightMargin;
if (ped->cch)
{
//
// Clear from end of text to end of window.
//
// We only need the y position. Since with an LPK loaded
// calculating the x position is an intensive job, just
// call EditML_IchToYPos.
//
if (ped->pLpkEditCallout)
{
xyPos.y = EditML_IchToYPos(ped, ped->cch, FALSE);
}
else
{
EditML_IchToXYPos(ped, hdc, ped->cch, FALSE, &xyPos);
}
rc.top = xyPos.y + ped->lineHeight;
}
InvalidateRect(ped->hwnd, &rc, TRUE);
Edit_ReleaseDC(ped, hdc, FALSE);
EditML_EnsureCaretVisible(ped);
}
ped->fDirty = TRUE;
Edit_NotifyParent(ped, EN_CHANGE);
if (cchDelete)
{
NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER);
}
return cchDelete;
}
//---------------------------------------------------------------------------//
//
// EditML_InsertchLine AorW
//
// Inserts the line iline and sets its starting character index to be
// ich. All the other line indices are moved up. Returns TRUE if successful
// else FALSE and notifies the parent that there was no memory.
//
BOOL EditML_InsertchLine(PED ped, ICH iLine, ICH ich, BOOL fUserTyping)
{
DWORD dwSize;
if (fUserTyping && iLine < ped->cLines)
{
ped->chLines[iLine] = ich;
return TRUE;
}
dwSize = (ped->cLines + 2) * sizeof(int);
if (dwSize > UserLocalSize(ped->chLines))
{
LPICH hResult;
//
// Grow the line index buffer
//
dwSize += LINEBUMP * sizeof(int);
hResult = (LPICH)UserLocalReAlloc(ped->chLines, dwSize, 0);
if (!hResult)
{
Edit_NotifyParent(ped, EN_ERRSPACE);
return FALSE;
}
ped->chLines = hResult;
}
//
// Move indices starting at iLine up
//
if (ped->cLines != iLine)
{
RtlMoveMemory(&ped->chLines[iLine + 1], &ped->chLines[iLine],
(ped->cLines - iLine) * sizeof(int));
}
ped->cLines++;
ped->chLines[iLine] = ich;
return TRUE;
}
//---------------------------------------------------------------------------//
//
// EditML_ShiftchLines AorW
//
// Move the starting index of all lines iLine or greater by delta
// bytes.
//
void EditML_ShiftchLines(PED ped, ICH iLine, int delta)
{
if (iLine < ped->cLines)
{
//
// Just add delta to the starting point of each line after iLine
//
for (; iLine < ped->cLines; iLine++)
{
ped->chLines[iLine] += delta;
}
}
}
//---------------------------------------------------------------------------//
//
// EditML_BuildchLines AorW
//
// Rebuilds the start of line array (ped->chLines) starting at line
// number ichLine.
//
void EditML_BuildchLines( PED ped, ICH iLine, int cchDelta, BOOL fUserTyping, PLONG pll, PLONG phl)
{
PSTR ptext; // Starting address of the text
//
// We keep these ICH's so that we can Unlock ped->hText when we have to grow
// the chlines array. With large text handles, it becomes a problem if we
// have a locked block in the way.
//
ICH ichLineStart;
ICH ichLineEnd;
ICH ichLineEndBeforeCRLF;
ICH ichCRLF;
ICH cch;
HDC hdc;
BOOL fLineBroken = FALSE; // Initially, no new line breaks are made
ICH minCchBreak;
ICH maxCchBreak;
BOOL fOnDelimiter;
if (!ped->cch)
{
ped->maxPixelWidth = 0;
ped->xOffset = 0;
ped->ichScreenStart = 0;
ped->cLines = 1;
if (pll)
{
*pll = 0;
}
if (phl)
{
*phl = 0;
}
goto UpdateScroll;
}
if (fUserTyping && cchDelta)
{
EditML_ShiftchLines(ped, iLine + 1, cchDelta);
}
hdc = Edit_GetDC(ped, TRUE);
if (!iLine && !cchDelta && !fUserTyping)
{
//
// Reset maxpixelwidth only if we will be running through the whole
// text. Better too long than too short.
//
ped->maxPixelWidth = 0;
//
// Reset number of lines in text since we will be running through all
// the text anyway...
//
ped->cLines = 1;
}
//
// Set min and max line built to be the starting line
//
minCchBreak = maxCchBreak = (cchDelta ? ped->chLines[iLine] : 0);
ptext = Edit_Lock(ped);
ichCRLF = ichLineStart = ped->chLines[iLine];
while (ichLineStart < ped->cch)
{
if (ichLineStart >= ichCRLF)
{
ichCRLF = ichLineStart;
//
// Move ichCRLF ahead to either the first CR or to the end of text.
//
if (ped->fAnsi)
{
while (ichCRLF < ped->cch)
{
if (*(ptext + ichCRLF) == 0x0D)
{
if (*(ptext + ichCRLF + 1) == 0x0A ||
*(WORD UNALIGNED *)(ptext + ichCRLF + 1) == 0x0A0D)
{
break;
}
}
ichCRLF++;
}
}
else
{
LPWSTR pwtext = (LPWSTR)ptext;
while (ichCRLF < ped->cch)
{
if (*(pwtext + ichCRLF) == 0x0D)
{
if (*(pwtext + ichCRLF + 1) == 0x0A ||
*(DWORD UNALIGNED *)(pwtext + ichCRLF + 1) == 0x000A000D)
{
break;
}
}
ichCRLF++;
}
}
}
if (!ped->fWrap)
{
UINT LineWidth;
//
// If we are not word wrapping, line breaks are signified by CRLF.
//
//
// If we cut off the line at MAXLINELENGTH, we should
// adjust ichLineEnd.
//
if ((ichCRLF - ichLineStart) <= MAXLINELENGTH)
{
ichLineEnd = ichCRLF;
}
else
{
ichLineEnd = ichLineStart + MAXLINELENGTH;
if (ped->fAnsi && ped->fDBCS)
{
ichLineEnd = Edit_AdjustIch( ped, (PSTR)ptext, ichLineEnd);
}
}
//
// We will keep track of what the longest line is for the horizontal
// scroll bar thumb positioning.
//
if (ped->pLpkEditCallout)
{
LineWidth = ped->pLpkEditCallout->EditGetLineWidth(
(PED0)ped, hdc, ptext + ichLineStart*ped->cbChar,
ichLineEnd - ichLineStart);
}
else
{
LineWidth = EditML_GetLineWidth(hdc, ptext + ichLineStart * ped->cbChar,
ichLineEnd - ichLineStart,
ped);
}
ped->maxPixelWidth = max(ped->maxPixelWidth,(int)LineWidth);
}
else
{
//
// Check if the width of the edit control is non-zero;
// a part of the fix for Bug #7402 -- SANKAR -- 01/21/91 --
//
if(ped->rcFmt.right > ped->rcFmt.left)
{
//
// Find the end of the line based solely on text extents
//
if (ped->pLpkEditCallout)
{
ichLineEnd = ichLineStart +
ped->pLpkEditCallout->EditCchInWidth(
(PED0)ped, hdc, ptext + ped->cbChar*ichLineStart,
ichCRLF - ichLineStart,
ped->rcFmt.right - ped->rcFmt.left);
}
else
{
if (ped->fAnsi)
{
ichLineEnd = ichLineStart +
Edit_CchInWidth(ped, hdc,
ptext + ichLineStart,
ichCRLF - ichLineStart,
ped->rcFmt.right - ped->rcFmt.left,
TRUE);
}
else
{
ichLineEnd = ichLineStart +
Edit_CchInWidth(ped, hdc,
(LPSTR)((LPWSTR)ptext + ichLineStart),
ichCRLF - ichLineStart,
ped->rcFmt.right - ped->rcFmt.left,
TRUE);
}
}
}
else
{
ichLineEnd = ichLineStart;
}
if (ichLineEnd == ichLineStart && ichCRLF - ichLineStart)
{
//
// Maintain a minimum of one char per line
// Since it might be a double byte char, so calling Edit_NextIch.
//
ichLineEnd = Edit_NextIch(ped, NULL, ichLineEnd);
}
//
// Now starting from ichLineEnd, if we are not at a hard line break,
// then if we are not at a space AND the char before us is
// not a space,(OR if we are at a CR) we will look word left for the
// start of the word to break at.
// This change was done for TWO reasons:
// 1. If we are on a delimiter, no need to look word left to break at.
// 2. If the previous char is a delimter, we can break at current char.
// Change done by -- SANKAR --01/31/91--
//
if (ichLineEnd != ichCRLF)
{
if(ped->lpfnNextWord)
{
fOnDelimiter = (CALLWORDBREAKPROC(*ped->lpfnNextWord, ptext,
ichLineEnd, ped->cch, WB_ISDELIMITER) ||
CALLWORDBREAKPROC(*ped->lpfnNextWord, ptext, ichLineEnd - 1,
ped->cch, WB_ISDELIMITER));
//
// This change was done for FOUR reasons:
//
// 1. If we are on a delimiter, no need to look word left to break at.
// 2. If we are on a double byte character, we can break at current char.
// 3. If the previous char is a delimter, we can break at current char.
// 4. If the previous char is a double byte character, we can break at current char.
//
}
else if (ped->fAnsi)
{
fOnDelimiter = (ISDELIMETERA(*(ptext + ichLineEnd)) ||
Edit_IsDBCSLeadByte(ped, *(ptext + ichLineEnd)));
if (!fOnDelimiter)
{
PSTR pPrev = Edit_AnsiPrev(ped,ptext,ptext+ichLineEnd);
fOnDelimiter = ISDELIMETERA(*pPrev) ||
Edit_IsDBCSLeadByte(ped,*pPrev);
}
}
else
{
fOnDelimiter = (ISDELIMETERW(*((LPWSTR)ptext + ichLineEnd)) ||
Edit_IsFullWidth(CP_ACP,*((LPWSTR)ptext + ichLineEnd)) ||
ISDELIMETERW(*((LPWSTR)ptext + ichLineEnd - 1)) ||
Edit_IsFullWidth(CP_ACP,*((LPWSTR)ptext + ichLineEnd - 1)));
}
if (!fOnDelimiter ||
(ped->fAnsi && *(ptext + ichLineEnd) == 0x0D) ||
(!ped->fAnsi && *((LPWSTR)ptext + ichLineEnd) == 0x0D))
{
if (ped->lpfnNextWord != NULL)
{
cch = CALLWORDBREAKPROC(*ped->lpfnNextWord, (LPSTR)ptext, ichLineEnd,
ped->cch, WB_LEFT);
}
else
{
ped->fCalcLines = TRUE;
Edit_Word(ped, ichLineEnd, TRUE, &cch, NULL);
ped->fCalcLines = FALSE;
}
if (cch > ichLineStart)
{
ichLineEnd = cch;
}
//
// Now, if the above test fails, it means the word left goes
// back before the start of the line ie. a word is longer
// than a line on the screen. So, we just fit as much of
// the word on the line as possible. Thus, we use the
// pLineEnd we calculated solely on width at the beginning
// of this else block...
//
}
}
}
#if 0
if (!ISDELIMETERAW((*(ptext + (ichLineEnd - 1)*ped->cbChar))) && ISDELIMETERAW((*(ptext + ichLineEnd*ped->cbChar)))) #ERROR
if ((*(ptext + ichLineEnd - 1) != ' ' &&
*(ptext + ichLineEnd - 1) != VK_TAB) &&
(*(ptext + ichLineEnd) == ' ' ||
*(ptext + ichLineEnd) == VK_TAB))
#endif
if (AWCOMPARECHAR(ped,ptext + ichLineEnd * ped->cbChar, ' ') ||
AWCOMPARECHAR(ped,ptext + ichLineEnd * ped->cbChar, VK_TAB))
{
//
// Swallow the space at the end of a line.
//
if (ichLineEnd < ped->cch)
{
ichLineEnd++;
}
}
//
// Skip over crlf or crcrlf if it exists. Thus, ichLineEnd is the first
// character in the next line.
//
ichLineEndBeforeCRLF = ichLineEnd;
if (ped->fAnsi)
{
if (ichLineEnd < ped->cch && *(ptext + ichLineEnd) == 0x0D)
{
ichLineEnd += 2;
}
//
// Skip over CRCRLF
//
if (ichLineEnd < ped->cch && *(ptext + ichLineEnd) == 0x0A)
{
ichLineEnd++;
}
}
else
{
if (ichLineEnd < ped->cch && *(((LPWSTR)ptext) + ichLineEnd) == 0x0D)
{
ichLineEnd += 2;
}
//
// Skip over CRCRLF
//
if (ichLineEnd < ped->cch && *(((LPWSTR)ptext) + ichLineEnd) == 0x0A)
{
ichLineEnd++;
TraceMsg(TF_STANDARD, "Edit: Skip over CRCRLF");
}
}
#if DBG
if (ichLineEnd > ped->cch)
{
TraceMsg(TF_STANDARD, "Edit: ichLineEnd (%d)> ped->cch (%d)", ichLineEnd, ped->cch);
}
#endif
//
// Now, increment iLine, allocate space for the next line, and set its
// starting point
//
iLine++;
if (!fUserTyping || (iLine > ped->cLines - 1) || (ped->chLines[iLine] != ichLineEnd))
{
//
// The line break occured in a different place than before.
//
if (!fLineBroken)
{
//
// Since we haven't broken a line before, just set the min
// break line.
//
fLineBroken = TRUE;
if (ichLineEndBeforeCRLF == ichLineEnd)
{
minCchBreak = maxCchBreak = (ichLineEnd ? ichLineEnd - 1 : 0);
}
else
{
minCchBreak = maxCchBreak = ichLineEndBeforeCRLF;
}
}
maxCchBreak = max(maxCchBreak, ichLineEnd);
Edit_Unlock(ped);
//
// Now insert the new line into the array
//
if (!EditML_InsertchLine(ped, iLine, ichLineEnd, (BOOL)(cchDelta != 0)))
{
goto EndUp;
}
ptext = Edit_Lock(ped);
}
else
{
maxCchBreak = ped->chLines[iLine];
//
// Quick escape
//
goto UnlockAndEndUp;
}
ichLineStart = ichLineEnd;
}
if (iLine != ped->cLines)
{
TraceMsg(TF_STANDARD, "Edit: chLines[%d] is being cleared.", iLine);
ped->cLines = iLine;
ped->chLines[ped->cLines] = 0;
}
//
// Note that we incremented iLine towards the end of the while loop so, the
// index, iLine, is actually equal to the line count
//
if (ped->cch && AWCOMPARECHAR(ped, ptext + (ped->cch - 1)*ped->cbChar, 0x0A) &&
ped->chLines[ped->cLines - 1] < ped->cch)
{
//
// Make sure last line has no crlf in it
//
if (!fLineBroken)
{
//
// Since we haven't broken a line before, just set the min break
// line.
//
fLineBroken = TRUE;
minCchBreak = ped->cch - 1;
}
maxCchBreak = max(maxCchBreak, ichLineEnd);
Edit_Unlock(ped);
EditML_InsertchLine(ped, iLine, ped->cch, FALSE);
EditML_SanityCheck(ped);
}
else
{
UnlockAndEndUp:
Edit_Unlock(ped);
}
EndUp:
Edit_ReleaseDC(ped, hdc, TRUE);
if (pll)
{
*pll = minCchBreak;
}
if (phl)
{
*phl = maxCchBreak;
}
UpdateScroll:
EditML_Scroll(ped, FALSE, ML_REFRESH, 0, TRUE);
EditML_Scroll(ped, TRUE, ML_REFRESH, 0, TRUE);
EditML_SanityCheck(ped);
return;
}
//---------------------------------------------------------------------------//
//
// EditML_Paint()
//
// Response to WM_PAINT message.
//
VOID EditML_Paint(PED ped, HDC hdc, LPRECT lprc)
{
HFONT hOldFont;
ICH imin;
ICH imax;
HBRUSH hbr;
BOOL fNeedDelete = FALSE;
//
// Do we need to draw the border ourself for old apps?
//
if (ped->fFlatBorder)
{
RECT rcT;
ULONG ulStyle;
INT cxBorder;
INT cyBorder;
INT cxFrame;
INT cyFrame;
ulStyle = GET_STYLE(ped);
cxBorder = GetSystemMetrics(SM_CXBORDER);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cxFrame = GetSystemMetrics(SM_CXFRAME);
cyFrame = GetSystemMetrics(SM_CYFRAME);
GetClientRect(ped->hwnd, &rcT);
if (ulStyle & WS_SIZEBOX)
{
InflateRect(&rcT, cxBorder - cxFrame, cyBorder - cyFrame);
}
DrawFrame(hdc, &rcT, 1, DF_WINDOWFRAME);
}
Edit_SetClip(ped, hdc, (BOOL) (ped->xOffset == 0));
if (ped->hFont)
{
hOldFont = SelectObject(hdc, ped->hFont);
}
if (!lprc)
{
//
// no partial rect given -- draw all text
//
imin = 0;
imax = ped->cch;
}
else
{
//
// only draw pertinent text
//
imin = (ICH) EditML_MouseToIch(ped, hdc, ((LPPOINT) &lprc->left), NULL) - 1;
if (imin == -1)
{
imin = 0;
}
//
// HACK_ALERT:
// The 3 is required here because, EditML_MouseToIch() returns decremented
// value; We must fix EditML_MouseToIch.
//
imax = (ICH) EditML_MouseToIch(ped, hdc, ((LPPOINT) &lprc->right), NULL) + 3;
if (imax > ped->cch)
{
imax = ped->cch;
}
}
hbr = Edit_GetBrush(ped, hdc, &fNeedDelete);
if (hbr)
{
RECT rc;
GetClientRect(ped->hwnd, &rc);
FillRect(hdc, &rc, hbr);
if (fNeedDelete)
{
DeleteObject(hbr);
}
}
EditML_DrawText(ped, hdc, imin, imax, FALSE);
if (ped->hFont)
{
SelectObject(hdc, hOldFont);
}
}
//---------------------------------------------------------------------------//
//
// EditML_KeyDown AorW
//
// Handles cursor movement and other VIRT KEY stuff. keyMods allows
// us to make EditML_KeyDownHandler calls and specify if the modifier keys (shift
// and control) are up or down. 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 EditML_KeyDown(PED ped, UINT virtKeyCode, int keyMods)
{
HDC hdc;
BOOL prevLine;
POINT mousePt;
int defaultDlgId;
int iScrollAmt;
//
// Variables we will use for redrawing the updated text
//
//
// new selection is specified by newMinSel, newMaxSel
//
ICH newMaxSel = ped->ichMaxSel;
ICH newMinSel = ped->ichMinSel;
//
// Flags for drawing the updated text
//
BOOL changeSelection = FALSE;
//
// 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;
if (ped->fMouseDown)
{
//
// If we are in the middle of a mousedown command, don't do anything.
//
return;
}
if (ped->hwndBalloon)
{
Edit_HideBalloonTip(ped->hwnd);
}
scState = Edit_GetModKeys(keyMods);
switch (virtKeyCode)
{
case VK_ESCAPE:
if (ped->fInDialogBox)
{
//
// This condition is removed because, if the dialogbox does not
// have a CANCEL button and if ESC is hit when focus is on a
// ML edit control the dialogbox must close whether it has cancel
// button or not to be consistent with SL edit control;
// DefDlgProc takes care of the disabled CANCEL button case.
// Fix for Bug #4123 -- 02/07/91 -- SANKAR --
//
#if 0
if (GetDlgItem(ped->hwndParent, IDCANCEL))
#endif
//
// User hit ESC...Send a close message (which in turn sends a
// cancelID to the app in DefDialogProc...
//
PostMessage(ped->hwndParent, WM_CLOSE, 0, 0L);
}
return;
case VK_RETURN:
if (ped->fInDialogBox)
{
//
// If this multiline edit control is in a dialog box, then we want
// the RETURN key to be sent to the default dialog button (if there
// is one). CTRL-RETURN will insert a RETURN into the text. Note
// that CTRL-RETURN automatically translates into a linefeed (0x0A)
// and in the EditML_CharHandler, we handle this as if a return was
// entered.
//
if (scState != CTRLDOWN)
{
if (GET_STYLE(ped) & ES_WANTRETURN)
{
//
// This edit control wants cr to be inserted so break out of
// case.
//
return;
}
defaultDlgId = (int)(DWORD)LOWORD(SendMessage(ped->hwndParent,
DM_GETDEFID, 0, 0L));
if (defaultDlgId)
{
HWND hwnd = GetDlgItem(ped->hwndParent, defaultDlgId);
if (hwnd)
{
SendMessage(ped->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwnd, 1L);
if (!ped->fFocus)
{
PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0L);
}
}
}
}
return;
}
break;
case VK_TAB:
//
// If this multiline edit control is in a dialog box, then we want the
// TAB key to take you to the next control, shift TAB to take you to the
// previous control. We always want CTRL-TAB to insert a tab into the
// edit control regardless of weather or not we're in a dialog box.
//
if (scState == CTRLDOWN)
{
EditML_Char(ped, virtKeyCode, keyMods);
}
else if (ped->fInDialogBox)
{
SendMessage(ped->hwndParent, WM_NEXTDLGCTL, scState == SHFTDOWN, 0L);
}
return;
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
{
if (ped->pLpkEditCallout)
{
ped->ichCaret = Edit_MoveSelectionRestricted(ped, ped->ichCaret, TRUE);
}
else
{
//
// Move caret char left
//
ped->ichCaret = Edit_MoveSelection(ped, ped->ichCaret, TRUE);
}
}
//
// 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_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 = Edit_MoveSelectionRestricted(ped, ped->ichCaret, FALSE);
}
else
{
ped->ichCaret = Edit_MoveSelection(ped, ped->ichCaret, FALSE);
}
}
//
// 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_UP:
case VK_DOWN:
if (ped->cLines - 1 != ped->iCaretLine &&
ped->ichCaret == ped->chLines[ped->iCaretLine + 1])
{
prevLine = TRUE;
}
else
{
prevLine = FALSE;
}
hdc = Edit_GetDC(ped, TRUE);
EditML_IchToXYPos(ped, hdc, ped->ichCaret, prevLine, &mousePt);
Edit_ReleaseDC(ped, hdc, TRUE);
mousePt.y += 1 + (virtKeyCode == VK_UP ? -ped->lineHeight : ped->lineHeight);
if (!(scState & CTRLDOWN))
{
//
// Send fake mouse messages to handle this
// If VK_SHIFT is down, extend selection & move caret up/down
// 1 line. Otherwise, clear selection & move caret.
//
EditML_MouseMotion(ped, WM_LBUTTONDOWN,
!(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
EditML_MouseMotion(ped, WM_LBUTTONUP,
!(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
}
break;
case VK_HOME:
//
// Update caret.
//
if (scState & CTRLDOWN)
{
//
// Move caret to beginning of text.
//
ped->ichCaret = 0;
}
else
{
//
// Move caret to beginning of line.
//
ped->ichCaret = ped->chLines[ped->iCaretLine];
}
//
// Update selection.
//
newMinSel = ped->ichCaret;
if (scState & SHFTDOWN)
{
if (MaxEqCar && !MinEqMax)
{
if (scState & CTRLDOWN)
{
newMaxSel = ped->ichMinSel;
}
else
{
newMinSel = ped->ichMinSel;
newMaxSel = ped->ichCaret;
}
}
}
else
{
//
// Clear selection
//
newMaxSel = ped->ichCaret;
}
changeSelection = TRUE;
break;
case VK_END:
//
// Update caret.
//
if (scState & CTRLDOWN)
{
//
// Move caret to end of text.
//
ped->ichCaret = ped->cch;
}
else
{
//
// Move caret to end of line.
//
ped->ichCaret = ped->chLines[ped->iCaretLine] +
EditML_Line(ped, ped->iCaretLine);
}
//
// Update selection.
//
newMaxSel = ped->ichCaret;
if (scState & SHFTDOWN)
{
if (MinEqCar && !MinEqMax)
{
//
// Reduce selection
//
if (scState & CTRLDOWN)
{
newMinSel = ped->ichMaxSel;
}
else
{
newMinSel = ped->ichCaret;
newMaxSel = ped->ichMaxSel;
}
}
}
else
{
//
// Clear selection
//
newMinSel = ped->ichCaret;
}
changeSelection = TRUE;
break;
//
// FE_IME // EC_INSERT_COMPOSITION_CHAR : EditML_KeyDown() : VK_HANJA support
//
case VK_HANJA:
if ( HanjaKeyHandler( ped ) )
{
changeSelection = TRUE;
newMinSel = ped->ichCaret;
newMaxSel = ped->ichCaret + (ped->fAnsi ? 2 : 1);
}
break;
case VK_PRIOR:
case VK_NEXT:
if (!(scState & CTRLDOWN))
{
//
// Vertical scroll by one visual screen
//
hdc = Edit_GetDC(ped, TRUE);
EditML_IchToXYPos(ped, hdc, ped->ichCaret, FALSE, &mousePt);
Edit_ReleaseDC(ped, hdc, TRUE);
mousePt.y += 1;
SendMessage(ped->hwnd, WM_VSCROLL, virtKeyCode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0L);
//
// Move the cursor there
//
EditML_MouseMotion(ped, WM_LBUTTONDOWN, !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
EditML_MouseMotion(ped, WM_LBUTTONUP, !(scState & SHFTDOWN) ? 0 : MK_SHIFT, &mousePt);
}
else
{
//
// Horizontal scroll by one screenful minus one char
//
iScrollAmt = ((ped->rcFmt.right - ped->rcFmt.left) / ped->aveCharWidth) - 1;
if (virtKeyCode == VK_PRIOR)
{
//
// For previous page
//
iScrollAmt *= -1;
}
SendMessage(ped->hwnd, WM_HSCROLL, MAKELONG(EM_LINESCROLL, iScrollAmt), 0);
break;
}
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 send a backspace message...
//
if (ped->pLpkEditCallout)
{
ped->ichMinSel = ped->ichCaret;
ped->ichMaxSel = Edit_MoveSelectionRestricted(ped, ped->ichCaret, FALSE);
}
else
{
ped->ichCaret = Edit_MoveSelection(ped, ped->ichCaret, FALSE);
ped->ichMaxSel = ped->ichMinSel = ped->ichCaret;
}
goto DeleteAnotherChar;
}
break;
case SHFTDOWN:
//
// CUT selection ie. remove and copy to clipboard, or if no
// selection, delete (clear) character left.
//
if (ped->ichMinSel == ped->ichMaxSel)
{
goto DeleteAnotherChar;
}
else
{
SendMessage(ped->hwnd, WM_CUT, (UINT)0, 0L);
}
break;
case CTRLDOWN:
//
// Clear selection, or delete to end of line if no selection
//
if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel))
{
ped->ichMaxSel = ped->ichCaret = ped->chLines[ped->iCaretLine] +
EditML_Line(ped, ped->iCaretLine);
}
break;
}
if (!(scState & SHFTDOWN) && (ped->ichMinSel != ped->ichMaxSel))
{
DeleteAnotherChar:
if (Is400Compat(UserGetVersion()))
{
EditML_Char(ped, VK_BACK, 0);
}
else
{
SendMessage(ped->hwnd, WM_CHAR, VK_BACK, 0);
}
}
//
// No need to update text or selection since BACKSPACE message does it
// for us.
//
break;
case VK_INSERT:
if (scState == CTRLDOWN || scState == SHFTDOWN)
{
//
// if CTRLDOWN Copy current selection to clipboard
//
//
// if SHFTDOWN Paste clipboard
//
SendMessage(ped->hwnd, (UINT)(scState == CTRLDOWN ? WM_COPY : WM_PASTE), 0, 0);
}
break;
}
if (changeSelection)
{
hdc = Edit_GetDC(ped, FALSE);
EditML_ChangeSelection(ped, hdc, newMinSel, newMaxSel);
//
// Set the caret's line
//
ped->iCaretLine = EditML_IchToLine(ped, ped->ichCaret);
if (virtKeyCode == VK_END &&
// Next line: Win95 Bug#11822, EditControl repaint (Sankar)
(ped->ichCaret == ped->chLines[ped->iCaretLine]) &&
ped->ichCaret < ped->cch &&
ped->fWrap && ped->iCaretLine > 0)
{
LPSTR pText = Edit_Lock(ped);
//
// Handle moving to the end of a word wrapped line. This keeps the
// cursor from falling to the start of the next line if we have word
// wrapped and there is no CRLF.
//
if ( ped->fAnsi )
{
if (*(WORD UNALIGNED *)(pText + ped->chLines[ped->iCaretLine] - 2) != 0x0A0D)
{
ped->iCaretLine--;
}
}
else
{
if (*(DWORD UNALIGNED *)(pText +
(ped->chLines[ped->iCaretLine] - 2)*ped->cbChar) != 0x000A000D)
{
ped->iCaretLine--;
}
}
Edit_Unlock(ped);
}
//
// Since drawtext sets the caret position
//
EditML_SetCaretPosition(ped, hdc);
Edit_ReleaseDC(ped, hdc, FALSE);
//
// Make sure we can see the cursor
//
EditML_EnsureCaretVisible(ped);
}
}
//---------------------------------------------------------------------------//
//
// EditML_Char
//
// Handles character and virtual key input
//
VOID EditML_Char(PED ped, DWORD keyValue, int keyMods)
{
WCHAR keyPress;
BOOL updateText = FALSE;
//
// keyValue is either:
// a Virtual Key (eg: VK_TAB, VK_ESCAPE, VK_BACK)
// a character (Unicode or "ANSI")
//
if (ped->fAnsi)
{
keyPress = LOBYTE(keyValue);
}
else
{
keyPress = LOWORD(keyValue);
}
if (ped->fMouseDown || keyPress == VK_ESCAPE)
{
//
// If we are in the middle of a mousedown command, don't do anything.
// Also, just ignore it if we get a translated escape key which happens
// with multiline edit controls in a dialog box.
//
return;
}
Edit_InOutReconversionMode(ped, FALSE);
{
int scState;
scState = Edit_GetModKeys(keyMods);
if (ped->fInDialogBox && scState != CTRLDOWN)
{
//
// If this multiline edit control is in a dialog box, then we want the
// TAB key to take you to the next control, shift TAB to take you to the
// previous control, and CTRL-TAB to insert a tab into the edit control.
// We moved the focus when we received the keydown message so we will
// ignore the TAB key now unless the ctrl key is down. Also, we want
// CTRL-RETURN to insert a return into the text and RETURN to be sent to
// the default button.
//
if (keyPress == VK_TAB || (keyPress == VK_RETURN && !(GET_STYLE(ped) & ES_WANTRETURN)))
{
return;
}
}
//
// Allow CTRL+C to copy from a read only edit control
// Ignore all other keys in read only controls
//
if ((ped->fReadOnly) && !((keyPress == 3) && (scState == CTRLDOWN)))
{
return;
}
}
switch (keyPress)
{
case 0x0A:
// linefeed
keyPress = VK_RETURN;
//
// FALL THRU
//
case VK_RETURN:
case VK_TAB:
case VK_BACK:
DeleteSelection:
if (EditML_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 (Is400Compat(UserGetVersion()) && 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;
}
//
// Handle key codes
//
switch(keyPress)
{
UINT msg;
// Ctrl+Z == Undo
case 26:
msg = WM_UNDO;
goto SendEditingMessage;
break;
// Ctrl+X == Cut
case 24:
if (ped->ichMinSel == ped->ichMaxSel)
{
goto IllegalChar;
}
else
{
msg = WM_CUT;
goto SendEditingMessage;
}
break;
// Ctrl+C == Copy
case 3:
msg = WM_COPY;
goto SendEditingMessage;
break;
// Ctrl+V == Paste
case 22:
msg = WM_PASTE;
SendEditingMessage:
SendMessage(ped->hwnd, msg, 0, 0L);
break;
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 = Edit_MoveSelection(ped, ped->ichCaret, TRUE);
EditML_DeleteText(ped);
}
break;
default:
if (keyPress == VK_RETURN)
{
if (ped->fAnsi)
{
keyValue = 0x0A0D;
}
else
{
keyValue = 0x000A000D;
}
}
if ( keyPress >= TEXT(' ')
|| keyPress == VK_RETURN
|| keyPress == VK_TAB
|| keyPress == 0x1E // RS - Unicode block separator
|| keyPress == 0x1F // US - Unicode segment separator
)
{
// Don't hide the cursor if someone has capture.
if (GetCapture() == NULL)
{
SetCursor(NULL);
}
if (ped->fAnsi)
{
//
// check if it's a leading byte of double byte character
//
if (Edit_IsDBCSLeadByte(ped,(BYTE)keyPress))
{
int DBCSkey;
DBCSkey = DbcsCombine(ped->hwnd, keyPress);
if ( DBCSkey != 0)
{
keyValue = DBCSkey;
}
}
EditML_InsertText(ped, (LPSTR)&keyValue, HIBYTE(keyValue) ? 2 : 1, TRUE);
}
else
{
EditML_InsertText(ped, (LPSTR)&keyValue, HIWORD(keyValue) ? 2 : 1, TRUE);
}
}
else
{
IllegalChar:
MessageBeep(0);
}
break;
}
}
//---------------------------------------------------------------------------//
//
// EditML_PasteText AorW
//
// Pastes a line of text from the clipboard into the edit control
// starting at ped->ichCaret. 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 EditML_PasteText(PED ped)
{
HANDLE hData;
LPSTR lpchClip;
ICH cchAdded = 0;
HCURSOR hCursorOld;
#ifdef UNDO_CLEANUP // #ifdef Added in Chicago - johnl
if (!ped->fAutoVScroll)
{
//
// Empty the undo buffer if this edit control limits the amount of text
// the user can add to the window rect. This is so that we can undo this
// operation if doing in causes us to exceed the window boundaries.
//
Edit_EmptyUndo(ped);
}
#endif
hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
if (!OpenClipboard(ped->hwnd))
{
goto PasteExitNoCloseClip;
}
if (!(hData = GetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT)) ||
(GlobalFlags(hData) == GMEM_INVALID_HANDLE))
{
TraceMsg(TF_STANDARD, "Edit: EditML_PasteText(): couldn't get a valid handle(%x)", hData);
goto PasteExit;
}
//
// See if any text should be deleted
//
EditML_DeleteText(ped);
lpchClip = GlobalLock(hData);
if (lpchClip == NULL)
{
TraceMsg(TF_STANDARD, "Edit: EditML_PasteText: USERGLOBALLOCK(%x) failed.", hData);
goto PasteExit;
}
//
// Get the length of the addition.
//
if (ped->fAnsi)
{
cchAdded = strlen(lpchClip);
}
else
{
cchAdded = wcslen((LPWSTR)lpchClip);
}
//
// Insert the text (EditML_InsertText checks line length)
//
cchAdded = EditML_InsertText(ped, lpchClip, cchAdded, FALSE);
GlobalUnlock(hData);
PasteExit:
CloseClipboard();
PasteExitNoCloseClip:
SetCursor(hCursorOld);
return cchAdded;
}
//---------------------------------------------------------------------------//
VOID EditML_MouseMotion(PED ped, UINT message, UINT virtKeyDown, LPPOINT mousePt)
{
BOOL fChangedSel = FALSE;
UINT dtScroll = GetDoubleClickTime() / 5;
HDC hdc = Edit_GetDC(ped, TRUE);
ICH ichMaxSel = ped->ichMaxSel;
ICH ichMinSel = ped->ichMinSel;
ICH mouseCch;
ICH mouseLine;
int i, j;
LONG ll, lh;
mouseCch = EditML_MouseToIch(ped, hdc, mousePt, &mouseLine);
//
// Save for timer
//
ped->ptPrevMouse = *mousePt;
ped->prevKeys = virtKeyDown;
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.
// LiZ -- 5/5/93
//
if (ped->fAnsi && ped->fDBCS)
{
LPSTR pText = Edit_Lock(ped);
Edit_Word(ped,ped->ichCaret,
Edit_IsDBCSLeadByte(ped, *(pText+(ped->ichCaret)))
? FALSE :
(ped->ichCaret == ped->chLines[ped->iCaretLine]
? FALSE : TRUE), &ll, &lh);
Edit_Unlock(ped);
}
else
{
Edit_Word(ped, mouseCch, !(mouseCch == ped->chLines[mouseLine]), &ll, &lh);
}
if (!(virtKeyDown & MK_SHIFT))
{
//
// If shift key isn't down, move caret to mouse point and clear
// old selection
//
ichMinSel = ll;
ichMaxSel = ped->ichCaret = lh;
}
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)
{
ichMinSel = ped->ichCaret = ll;
Edit_Word(ped, ichMaxSel, TRUE, &ll, &lh);
}
else
{
ichMaxSel = ped->ichCaret = lh;
Edit_Word(ped, ichMinSel, FALSE, &ll, &lh);
}
}
ped->ichStartMinSel = ll;
ped->ichStartMaxSel = lh;
goto InitDragSelect;
case WM_MOUSEMOVE:
if (ped->fMouseDown)
{
//
// Set the system timer to automatically scroll when mouse is
// outside of the client rectangle. Speed of scroll depends on
// distance from window.
//
i = mousePt->y < 0 ? -mousePt->y : mousePt->y - ped->rcFmt.bottom;
j = dtScroll - ((UINT)i << 4);
if (j < 1)
{
j = 1;
}
SetTimer(ped->hwnd, IDSYS_SCROLL, (UINT)j, NULL);
fChangedSel = TRUE;
//
// Extend selection, move caret right
//
if (ped->ichStartMinSel || ped->ichStartMaxSel)
{
//
// We're in WORD SELECT mode
//
BOOL fReverse = (mouseCch <= ped->ichStartMinSel);
Edit_Word(ped, mouseCch, !fReverse, &ll, &lh);
if (fReverse)
{
ichMinSel = ped->ichCaret = ll;
ichMaxSel = ped->ichStartMaxSel;
}
else
{
ichMinSel = ped->ichStartMinSel;
ichMaxSel = ped->ichCaret = lh;
}
}
else if ((ped->ichMinSel == ped->ichCaret) &&
(ped->ichMinSel != ped->ichMaxSel))
{
//
// Reduce selection extent
//
ichMinSel = ped->ichCaret = mouseCch;
}
else
{
//
// Extend selection extent
//
ichMaxSel = ped->ichCaret = mouseCch;
}
ped->iCaretLine = mouseLine;
}
break;
case WM_LBUTTONDOWN:
ll = lh = mouseCch;
if (!(virtKeyDown & MK_SHIFT))
{
//
// If shift key isn't down, move caret to mouse point and clear
// old selection
//
ichMinSel = ichMaxSel = ped->ichCaret = mouseCch;
}
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)
{
ichMinSel = ped->ichCaret = mouseCch;
}
else
{
ichMaxSel = ped->ichCaret = mouseCch;
}
}
ped->ichStartMinSel = ped->ichStartMaxSel = 0;
InitDragSelect:
ped->iCaretLine = mouseLine;
ped->fMouseDown = FALSE;
SetCapture(ped->hwnd);
ped->fMouseDown = TRUE;
fChangedSel = TRUE;
//
// Set the timer so that we can scroll automatically when the mouse
// is moved outside the window rectangle.
//
SetTimer(ped->hwnd, IDSYS_SCROLL, dtScroll, NULL);
break;
case WM_LBUTTONUP:
if (ped->fMouseDown)
{
//
// Kill the timer so that we don't do auto mouse moves anymore
//
KillTimer(ped->hwnd, IDSYS_SCROLL);
ReleaseCapture();
EditML_SetCaretPosition(ped, hdc);
ped->fMouseDown = FALSE;
}
break;
}
if (fChangedSel)
{
EditML_ChangeSelection(ped, hdc, ichMinSel, ichMaxSel);
EditML_EnsureCaretVisible(ped);
}
Edit_ReleaseDC(ped, hdc, TRUE);
if (!ped->fFocus && (message == WM_LBUTTONDOWN))
{
//
// If we don't have the focus yet, get it
//
SetFocus(ped->hwnd);
}
}
//---------------------------------------------------------------------------//
LONG EditML_Scroll(PED ped, BOOL fVertical, int cmd, int iAmt, BOOL fRedraw)
{
SCROLLINFO si;
int dx = 0;
int dy = 0;
BOOL fIncludeLeftMargin;
int newPos;
int oldPos;
BOOL fUp = FALSE;
UINT wFlag;
DWORD dwTime = 0;
if (fRedraw && (cmd != ML_REFRESH))
{
UpdateWindow(ped->hwnd);
}
if (ped->pLpkEditCallout && ped->fRtoLReading && !fVertical
&& ped->maxPixelWidth > ped->rcFmt.right - ped->rcFmt.left)
{
//
// Horizontal scoll of a right oriented window with a scrollbar.
// Map the logical xOffset to visual coordinates.
//
oldPos = ped->maxPixelWidth
- ((int)ped->xOffset + ped->rcFmt.right - ped->rcFmt.left);
}
else
{
oldPos = (int) (fVertical ? ped->ichScreenStart : ped->xOffset);
}
fIncludeLeftMargin = (ped->xOffset == 0);
switch (cmd)
{
case ML_REFRESH:
newPos = oldPos;
break;
case EM_GETTHUMB:
return oldPos;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
//
// If the edit contains more than 0xFFFF lines
// it means that the scrolbar can return a position
// that cannot fit in a WORD (16 bits), so use
// GetScrollInfo (which is slower) in this case.
//
if (ped->cLines < 0xFFFF)
{
newPos = iAmt;
}
else
{
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_TRACKPOS;
GetScrollInfo( ped->hwnd, SB_VERT, &si);
newPos = si.nTrackPos;
}
break;
case SB_TOP: // == SB_LEFT
newPos = 0;
break;
case SB_BOTTOM: // == SB_RIGHT
if (fVertical)
{
newPos = ped->cLines;
}
else
{
newPos = ped->maxPixelWidth;
}
break;
case SB_PAGEUP: // == SB_PAGELEFT
fUp = TRUE;
case SB_PAGEDOWN: // == SB_PAGERIGHT
if (fVertical)
{
iAmt = ped->ichLinesOnScreen - 1;
}
else
{
iAmt = (ped->rcFmt.right - ped->rcFmt.left) - 1;
}
if (iAmt == 0)
{
iAmt++;
}
if (fUp)
{
iAmt = -iAmt;
}
goto AddDelta;
case SB_LINEUP: // == SB_LINELEFT
fUp = TRUE;
case SB_LINEDOWN: // == SB_LINERIGHT
dwTime = iAmt;
iAmt = 1;
if (fUp)
{
iAmt = -iAmt;
}
// | |
// | FALL THRU |
// V V
case EM_LINESCROLL:
if (!fVertical)
{
iAmt *= ped->aveCharWidth;
}
AddDelta:
newPos = oldPos + iAmt;
break;
default:
return(0L);
}
if (fVertical)
{
if (si.nMax = ped->cLines)
{
si.nMax--;
}
if (!ped->hwndParent ||
TestWF(ped->hwndParent, WFWIN40COMPAT))
{
si.nPage = ped->ichLinesOnScreen;
}
else
{
si.nPage = 0;
}
wFlag = WS_VSCROLL;
}
else
{
si.nMax = ped->maxPixelWidth;
si.nPage = ped->rcFmt.right - ped->rcFmt.left;
wFlag = WS_HSCROLL;
}
if (TESTFLAG(GET_STYLE(ped), wFlag))
{
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nPos = newPos;
newPos = SetScrollInfo(ped->hwnd, fVertical ? SB_VERT : SB_HORZ,
&si, fRedraw);
}
else
{
//
// BOGUS -- this is duped code from ScrollBar code
// but it's for the case when we want to limit the position without
// actually having the scroll bar
//
int iMaxPos;
//
// Clip page to 0, range + 1
//
si.nPage = max(min((int)si.nPage, si.nMax + 1), 0);
iMaxPos = si.nMax - (si.nPage ? si.nPage - 1 : 0);
newPos = min(max(newPos, 0), iMaxPos);
}
oldPos -= newPos;
if (!oldPos)
{
return 0;
}
if (ped->pLpkEditCallout && ped->fRtoLReading && !fVertical
&& ped->maxPixelWidth > ped->rcFmt.right - ped->rcFmt.left)
{
//
// Map visual oldPos and newPos back to logical coordinates
//
newPos = ped->maxPixelWidth
- (newPos + ped->rcFmt.right - ped->rcFmt.left);
oldPos = -oldPos;
if (newPos<0)
{
//
// Compensate for scroll bar returning pos > max-page
//
oldPos += newPos;
newPos=0;
}
}
if (fVertical)
{
ped->ichScreenStart = newPos;
dy = oldPos * ped->lineHeight;
}
else
{
ped->xOffset = newPos;
dx = oldPos;
}
if (cmd != SB_THUMBTRACK)
{
//
// We don't want to notify the parent of thumbtracking since they might
// try to set the thumb position to something bogus.
// NOTEPAD used to be guilty of this -- but I rewrote it so it's not.
// The question is WHO ELSE does this? (jeffbog)
//
Edit_NotifyParent(ped, fVertical ? EN_VSCROLL : EN_HSCROLL);
}
if (fRedraw && IsWindowVisible(ped->hwnd))
{
RECT rc;
RECT rcUpdate;
RECT rcClipRect;
HDC hdc;
HBRUSH hbr = NULL;
BOOL fNeedDelete = FALSE;
GetClientRect(ped->hwnd, &rc);
CopyRect(&rcClipRect, &ped->rcFmt);
if (fVertical)
{
rcClipRect.left -= ped->wLeftMargin;
rcClipRect.right += ped->wRightMargin;
}
IntersectRect(&rc, &rc, &rcClipRect);
rc.bottom++;
//
// Chicago has this HideCaret but there doesn't appear to be a
// corresponding ShowCaret, so we lose the Caret under NT when the
// EC scrolls - Johnl
//
// HideCaret(ped->hwnd);
hdc = Edit_GetDC(ped, FALSE);
Edit_SetClip(ped, hdc, fIncludeLeftMargin);
if (ped->hFont)
{
SelectObject(hdc, ped->hFont);
}
hbr = Edit_GetBrush(ped, hdc, &fNeedDelete);
if (hbr && fNeedDelete)
{
DeleteObject(hbr);
}
if (ped->pLpkEditCallout && !fVertical)
{
//
// Horizontal scroll with complex script support
//
int xFarOffset = ped->xOffset + ped->rcFmt.right - ped->rcFmt.left;
rc = ped->rcFmt;
if (dwTime != 0)
{
ScrollWindowEx(ped->hwnd, ped->fRtoLReading ? -dx : dx, dy, NULL, NULL, NULL,
&rcUpdate, MAKELONG(SW_SMOOTHSCROLL | SW_SCROLLCHILDREN, dwTime));
}
else
{
ScrollDC(hdc, ped->fRtoLReading ? -dx : dx, dy,
&rc, &rc, NULL, &rcUpdate);
}
//
// Handle margins: Blank if clipped by horizontal scrolling,
// display otherwise.
//
if (ped->wLeftMargin)
{
rc.left = ped->rcFmt.left - ped->wLeftMargin;
rc.right = ped->rcFmt.left;
if ( (ped->format != ES_LEFT) // Always display margin for centred or far-aligned text
|| // Display LTR left margin if first character fully visible
(!ped->fRtoLReading && ped->xOffset == 0)
|| // Display RTL left margin if last character fully visible
(ped->fRtoLReading && xFarOffset >= ped->maxPixelWidth))
{
UnionRect(&rcUpdate, &rcUpdate, &rc);
}
else
{
ExtTextOutW(hdc, rc.left, rc.top,
ETO_CLIPPED | ETO_OPAQUE | ETO_GLYPH_INDEX,
&rc, L"", 0, 0L);
}
}
if (ped->wRightMargin)
{
rc.left = ped->rcFmt.right;
rc.right = ped->rcFmt.right + ped->wRightMargin;
if ( (ped->format != ES_LEFT) // Always display margin for centred or far-aligned text
|| // Display RTL right margin if first character fully visible
(ped->fRtoLReading && ped->xOffset == 0)
|| // Display LTR right margin if last character fully visible
(!ped->fRtoLReading && xFarOffset >= ped->maxPixelWidth))
{
UnionRect(&rcUpdate, &rcUpdate, &rc);
}
else
{
ExtTextOutW(hdc, rc.left, rc.top,
ETO_CLIPPED | ETO_OPAQUE | ETO_GLYPH_INDEX,
&rc, L"", 0, 0L);
}
}
}
else
{
if (dwTime != 0)
{
ScrollWindowEx(ped->hwnd, dx, dy, NULL, NULL, NULL,
&rcUpdate, MAKELONG(SW_SMOOTHSCROLL | SW_SCROLLCHILDREN, dwTime));
}
else
{
ScrollDC(hdc, dx, dy, &rc, &rc, NULL, &rcUpdate);
}
//
// If we need to wipe out the left margin area
//
if (ped->wLeftMargin && !fVertical)
{
//
// Calculate the rectangle to be wiped out
//
rc.right = rc.left;
rc.left = max(0, ped->rcFmt.left - (LONG)ped->wLeftMargin);
if (rc.left < rc.right)
{
if (fIncludeLeftMargin && (ped->xOffset != 0))
{
ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED | ETO_OPAQUE,
&rc, L"", 0, 0L);
}
else
{
if((!fIncludeLeftMargin) && (ped->xOffset == 0))
{
UnionRect(&rcUpdate, &rcUpdate, &rc);
}
}
}
}
}
EditML_SetCaretPosition(ped,hdc);
Edit_ReleaseDC(ped, hdc, FALSE);
InvalidateRect(ped->hwnd, &rcUpdate, TRUE);
UpdateWindow(ped->hwnd);
}
return MAKELONG(-oldPos, 1);
}
//---------------------------------------------------------------------------//
//
// EditML_SetFocus AorW
//
// Gives the edit control the focus and notifies the parent
// EN_SETFOCUS.
//
void EditML_SetFocus(PED ped)
{
HDC hdc;
INT cxCaret;
SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0);
if (!ped->fFocus)
{
ped->fFocus = TRUE;
InvalidateRect(ped->hwnd, NULL, TRUE);
hdc = Edit_GetDC(ped, TRUE);
//
// Draw the caret. We need to do this even if the window is hidden
// because in dlg box initialization time we may set the focus to a
// hidden edit control window. If we don't create the caret etc, it will
// never end up showing properly.
//
if (ped->pLpkEditCallout)
{
ped->pLpkEditCallout->EditCreateCaret((PED0)ped, hdc, cxCaret, ped->lineHeight, 0);
}
else
{
CreateCaret(ped->hwnd, (HBITMAP)NULL, cxCaret, ped->lineHeight);
}
ShowCaret(ped->hwnd);
EditML_SetCaretPosition(ped, hdc);
//
// Show the current selection. Only if the selection was hidden when we
// lost the focus, must we invert (show) it.
//
if (!ped->fNoHideSel && ped->ichMinSel != ped->ichMaxSel &&
IsWindowVisible(ped->hwnd))
{
EditML_DrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel, TRUE);
}
Edit_ReleaseDC(ped, hdc, TRUE);
}
#if 0
EditML_EnsureCaretVisible(ped);
#endif
//
// Notify parent we have the focus
//
Edit_NotifyParent(ped, EN_SETFOCUS);
}
//---------------------------------------------------------------------------//
//
// EditML_KillFocus AorW
//
// The edit control loses the focus and notifies the parent via
// EN_KILLFOCUS.
//
VOID EditML_KillFocus(PED ped)
{
HDC hdc;
//
// Reset the wheel delta count.
//
if (ped->fFocus)
{
ped->fFocus = 0;
//
// 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 &&
IsWindowVisible(ped->hwnd))) || ped->hTheme)
{
hdc = Edit_GetDC(ped, FALSE);
if ( !ped->hTheme )
{
EditML_DrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel, TRUE);
}
else
{
InvalidateRect(ped->hwnd, NULL, TRUE);
}
Edit_ReleaseDC(ped, hdc, FALSE);
}
#else
if (((!ped->fNoHideSel && ped->ichMinSel != ped->ichMaxSel &&
IsWindowVisible(ped->hwnd))))
{
hdc = Edit_GetDC(ped, FALSE);
EditML_DrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel, TRUE);
Edit_ReleaseDC(ped, hdc, FALSE);
}
#endif // _USE_DRAW_THEME_TEXT_
//
// Destroy the caret
//
DestroyCaret();
}
//
// Notify parent that we lost the focus.
//
Edit_NotifyParent(ped, EN_KILLFOCUS);
}
//---------------------------------------------------------------------------//
//
// EditML_EnsureCaretVisible AorW
//
// Scrolls the caret into the visible region.
// Returns TRUE if scrolling was done else return s FALSE.
//
BOOL EditML_EnsureCaretVisible(PED ped)
{
UINT iLineMax;
int xposition;
BOOL fPrevLine;
HDC hdc;
BOOL fVScroll = FALSE;
BOOL fHScroll = FALSE;
if (IsWindowVisible(ped->hwnd))
{
int iAmt;
int iFmtWidth = ped->rcFmt.right - ped->rcFmt.left;
if (ped->fAutoVScroll)
{
iLineMax = ped->ichScreenStart + ped->ichLinesOnScreen - 1;
if (fVScroll = (ped->iCaretLine > iLineMax))
{
iAmt = iLineMax;
}
else if (fVScroll = (ped->iCaretLine < ped->ichScreenStart))
{
iAmt = ped->ichScreenStart;
}
if (fVScroll)
{
EditML_Scroll(ped, TRUE, EM_LINESCROLL, ped->iCaretLine - iAmt, TRUE);
}
}
if (ped->fAutoHScroll && ((int) ped->maxPixelWidth > iFmtWidth))
{
POINT pt;
//
// Get the current position of the caret in pixels
//
if ((UINT) (ped->cLines - 1) != ped->iCaretLine &&
ped->ichCaret == ped->chLines[ped->iCaretLine + 1])
{
fPrevLine = TRUE;
}
else
{
fPrevLine = FALSE;
}
hdc = Edit_GetDC(ped,TRUE);
EditML_IchToXYPos(ped, hdc, ped->ichCaret, fPrevLine, &pt);
Edit_ReleaseDC(ped, hdc, TRUE);
xposition = pt.x;
//
// Remember, EditML_IchToXYPos returns coordinates with respect to the
// top left pixel displayed on the screen. Thus, if xPosition < 0,
// it means xPosition is less than current ped->xOffset.
//
iFmtWidth /= 3;
if (fHScroll = (xposition < ped->rcFmt.left))
{
//
// scroll to the left
//
iAmt = ped->rcFmt.left + iFmtWidth;
}
else if (fHScroll = (xposition > ped->rcFmt.right))
{
//
// scroll to the right
//
iAmt = ped->rcFmt.right - iFmtWidth;
}
if (fHScroll)
{
EditML_Scroll(ped, FALSE, EM_LINESCROLL, (xposition - iAmt) / ped->aveCharWidth, TRUE);
}
}
}
return fVScroll;
}
//---------------------------------------------------------------------------//
//
// EditML_WndProc
//
// Class procedure for all multi 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 EditML_WndProc only handles messages specific to multi line edit
// controls.
//
LRESULT EditML_WndProc(PED ped, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
LPRECT lprc;
POINT pt;
DWORD windowstyle;
static INT scWheelDelta;
switch (message)
{
case WM_INPUTLANGCHANGE:
if (ped && ped->fFocus && ped->pLpkEditCallout)
{
INT 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);
EditML_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)
//
EditML_Char(ped, (UINT)wParam, 0);
break;
case WM_ERASEBKGND:
{
RECT rc;
GetClientRect(ped->hwnd, &rc);
#ifdef _USE_DRAW_THEME_TEXT_
if (!ped->hTheme)
#endif // _USE_DRAW_THEME_TEXT_
{
HBRUSH hbr = NULL;
BOOL fNeedDelete = FALSE;
hbr = Edit_GetBrush(ped, (HDC)wParam, &fNeedDelete);
if (hbr)
{
FillRect((HDC)wParam, &rc, hbr);
if (fNeedDelete)
{
DeleteObject(hbr);
}
}
}
#ifdef _USE_DRAW_THEME_TEXT_
else
{
HRESULT hr;
INT iStateId = Edit_GetStateId(ped);
hr = DrawThemeBackground(ped->hTheme, (HDC)wParam, EP_EDITTEXT, iStateId, &rc, 0);
}
#endif // _USE_DRAW_THEME_TEXT_
return TRUE;
}
case WM_GETDLGCODE:
{
LONG code = DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS | DLGC_WANTALLKEYS;
//
// !!! JEFFBOG HACK !!!
// Only set Dialog Box Flag if GETDLGCODE message is generated by
// IsDialogMessage -- if so, the lParam will be a pointer to the
// message structure passed to IsDialogMessage; otherwise, lParam
// will be NULL. Reason for the HACK alert: the wParam & lParam
// for GETDLGCODE is still not clearly defined and may end up
// changing in a way that would throw this off
//
if (lParam)
{
// Mark ML edit ctrl as in a dialog box
ped->fInDialogBox = TRUE;
}
//
// 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 && (((LPMSG)lParam)->message == WM_SYSCHAR) &&
((DWORD)((LPMSG)lParam)->lParam & SYS_ALTERNATE) &&
((WORD)wParam == VK_BACK))
{
code |= DLGC_WANTMESSAGE;
}
return code;
}
case EM_SCROLL:
message = WM_VSCROLL;
//
// FALL THROUGH
//
case WM_HSCROLL:
case WM_VSCROLL:
return EditML_Scroll(ped, (message==WM_VSCROLL), LOWORD(wParam), HIWORD(wParam), TRUE);
case WM_MOUSEWHEEL:
{
UINT ucWheelScrollLines;
//
// Don't handle zoom and datazoom.
//
if (wParam & (MK_SHIFT | MK_CONTROL))
{
goto PassToDefaultWindowProc;
}
scWheelDelta -= (short) HIWORD(wParam);
windowstyle = GET_STYLE(ped);
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (LPVOID)&ucWheelScrollLines, 0);
if ( abs(scWheelDelta) >= WHEEL_DELTA &&
ucWheelScrollLines > 0 &&
(windowstyle & (WS_VSCROLL | WS_HSCROLL)))
{
int cLineScroll;
BOOL fVert;
int cPage;
if (windowstyle & WS_VSCROLL)
{
fVert = TRUE;
cPage = ped->ichLinesOnScreen;
}
else
{
fVert = FALSE;
cPage = (ped->rcFmt.right - ped->rcFmt.left) / ped->aveCharWidth;
}
//
// Limit a roll of one (1) WHEEL_DELTA to scroll one (1) page.
//
cLineScroll = (int) min(
(UINT) (max(1, (cPage - 1))),
ucWheelScrollLines);
cLineScroll *= (scWheelDelta / WHEEL_DELTA);
UserAssert(cLineScroll != 0);
scWheelDelta = scWheelDelta % WHEEL_DELTA;
EditML_Scroll(ped, fVert, EM_LINESCROLL, cLineScroll, TRUE);
}
break;
}
case WM_KEYDOWN:
//
// wParam - virt keycode of the given key
// lParam - modifiers such as repeat count etc. (not used)
//
EditML_KeyDown(ped, (UINT)wParam, 0);
break;
case WM_KILLFOCUS:
//
// wParam - handle of the window that receives the input focus
// lParam - not used
//
scWheelDelta = 0;
EditML_KillFocus(ped);
break;
case WM_CAPTURECHANGED:
//
// wParam -- unused
// lParam -- hwnd of window gaining capture.
//
if (ped->fMouseDown)
{
//
// We don't change the caret pos here. If this is happening
// due to button up, then we'll change the pos in the
// handler after ReleaseCapture(). Otherwise, just end
// gracefully because someone else has stolen capture out
// from under us.
//
ped->fMouseDown = FALSE;
KillTimer(ped->hwnd, IDSYS_SCROLL);
}
break;
case WM_SYSTIMER:
//
// This allows us to automatically scroll if the user holds the mouse
// outside the edit control window. We simulate mouse moves at timer
// intervals set in MouseMotionHandler.
//
if (ped->fMouseDown)
{
EditML_MouseMotion(ped, WM_MOUSEMOVE, ped->prevKeys, &ped->ptPrevMouse);
}
break;
case WM_MBUTTONDOWN:
EnterReaderMode(ped->hwnd);
break;
case WM_MOUSEMOVE:
UserAssert(ped->fMouseDown);
//
// FALL THROUGH
//
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);
EditML_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 EditML_Create(ped, (LPCREATESTRUCT)lParam);
case WM_PRINTCLIENT:
EditML_Paint(ped, (HDC) wParam, NULL);
break;
case WM_PAINT:
//
// wParam - can be hdc from subclassed paint
// lParam - not used
//
if (wParam)
{
hdc = (HDC)wParam;
lprc = NULL;
}
else
{
hdc = BeginPaint(ped->hwnd, &ps);
lprc = &ps.rcPaint;
}
if (IsWindowVisible(ped->hwnd))
{
EditML_Paint(ped, hdc, lprc);
}
if (!wParam)
{
EndPaint(ped->hwnd, &ps);
}
break;
case WM_PASTE:
//
// wParam - not used
// lParam - not used
//
if (!ped->fReadOnly)
{
EditML_PasteText(ped);
}
break;
case WM_SETFOCUS:
//
// wParam - handle of window that loses the input focus (may be NULL)
// lParam - not used
//
EditML_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);
break;
case EM_FMTLINES:
//
// wParam - indicates disposition of end-of-line chars. If non
// zero, the chars CR CR LF are placed at the end of a word
// wrapped line. If wParam is zero, the end of line chars are
// removed. This is only done when the user gets a handle (via
// EM_GETHANDLE) to the text. lParam - not used.
//
if (wParam)
{
EditML_InsertCrCrLf(ped);
}
else
{
EditML_StripCrCrLf(ped);
}
EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
return (LONG)(wParam != 0);
case EM_GETHANDLE:
//
// wParam - not used
// lParam - not used
//
//
// Returns a handle to the edit control's text.
//
//
// Null terminate the string. Note that we are guaranteed to have the
// memory for the NULL since Edit_InsertText allocates an extra
// WCHAR for the NULL terminator.
//
if (ped->fAnsi)
{
*(Edit_Lock(ped) + ped->cch) = 0;
}
else
{
*((LPWSTR)Edit_Lock(ped) + ped->cch) = 0;
}
Edit_Unlock(ped);
return ((LRESULT)ped->hText);
case EM_GETLINE:
//
// wParam - line number to copy (0 is first line)
// lParam - buffer to copy text to. First WORD is max # of bytes to
// copy
//
return EditML_GetLine(ped, (ICH)wParam, (ICH)*(WORD UNALIGNED *)lParam, (LPSTR)lParam);
case EM_LINEFROMCHAR:
//
// wParam - Contains the index value for the desired char in the text
// of the edit control. These are 0 based.
// lParam - not used
//
return (LRESULT)EditML_IchToLine(ped, (ICH)wParam);
case EM_LINEINDEX:
//
// wParam - specifies the desired line number where the number of the
// first line is 0. If linenumber = 0, the line with the caret is used.
// lParam - not used.
// This function return s the number of character positions that occur
// preceeding the first char in a given line.
//
return (LRESULT)EditML_LineIndex(ped, (ICH)wParam);
case EM_LINELENGTH:
//
// wParam - specifies the character index of a character in the
// specified line, where the first line is 0. If -1, the length
// of the current line (with the caret) is return ed not including the
// length of any selected text.
// lParam - not used
//
return (LRESULT)EditML_LineLength(ped, (ICH)wParam);
case EM_LINESCROLL:
//
// wParam - not used
// lParam - Contains the number of lines and char positions to scroll
//
EditML_Scroll(ped, TRUE, EM_LINESCROLL, (INT)lParam, TRUE);
EditML_Scroll(ped, FALSE, EM_LINESCROLL, (INT)wParam, TRUE);
break;
case EM_REPLACESEL:
//
// wParam - flag for 4.0+ apps saying whether to clear undo
// lParam - Points to a null terminated replacement text.
//
EditML_ReplaceSel(ped, (LPSTR)lParam);
if (!ped->f40Compat || !wParam)
{
Edit_EmptyUndo(Pundo(ped));
}
break;
case EM_SETHANDLE:
//
// wParam - contains a handle to the text buffer
// lParam - not used
//
EditML_SetHandle(ped, (HANDLE)wParam);
break;
case EM_SETRECT:
case EM_SETRECTNP:
//
// wParamLo -- not used
// lParam -- LPRECT with new formatting area
//
Edit_Size(ped, (LPRECT) lParam, (message != EM_SETRECTNP));
break;
case EM_SETSEL:
//
// wParam - Under 3.1, specifies if we should scroll caret into
// view or not. 0 == scroll into view. 1 == don't scroll
// lParam - starting pos in lowword ending pos in high word
//
// Under Win32, wParam is the starting pos, lParam is the
// ending pos, and the caret is not scrolled into view.
// The message EM_SCROLLCARET forces the caret to be scrolled
// into view.
//
EditML_SetSelection(ped, TRUE, (ICH)wParam, (ICH)lParam);
break;
case EM_SCROLLCARET:
//
// Scroll caret into view
//
EditML_EnsureCaretVisible(ped);
break;
case EM_GETFIRSTVISIBLELINE:
//
// Returns the first visible line for multiline edit controls.
//
return (LONG)ped->ichScreenStart;
case WM_SYSKEYDOWN:
if (((WORD)wParam == VK_BACK) && ((DWORD)lParam & SYS_ALTERNATE))
{
SendMessage(ped->hwnd, EM_UNDO, 0, 0L);
break;
}
goto PassToDefaultWindowProc;
case WM_UNDO:
case EM_UNDO:
return EditML_Undo(ped);
case EM_SETTABSTOPS:
//
// This sets the tab stop positions for multiline edit controls.
// wParam - Number of tab stops
// lParam - Far ptr to a UINT array containing the Tab stop positions
//
return EditML_SetTabStops(ped, (int)wParam, (LPINT)lParam);
case EM_POSFROMCHAR:
//
// wParam -- char index in text
// lParam -- not used
// This function returns the (x,y) position of the character
//
case EM_CHARFROMPOS:
//
// wParam -- unused
// lParam -- pt in client coordinates
// This function returns
// LOWORD: the position of the closest character
// to the passed in point. Beware of
// points not actually in the edit client...
// HIWORD: the index of the line the char is on
//
{
LONG xyPos;
LONG line;
hdc = Edit_GetDC(ped, TRUE);
if (message == EM_POSFROMCHAR)
{
EditML_IchToXYPos(ped, hdc, (ICH)wParam, FALSE, &pt);
xyPos = MAKELONG(pt.x, pt.y);
}
else
{
POINTSTOPOINT(pt, lParam);
xyPos = EditML_MouseToIch(ped, hdc, &pt, &line);
xyPos = MAKELONG(xyPos, line);
}
Edit_ReleaseDC(ped, hdc, TRUE);
return (LRESULT)xyPos;
}
case WM_SETREDRAW:
DefWindowProc(ped->hwnd, message, wParam, lParam);
if (wParam)
{
//
// Backwards compatability hack needed so that winraid's edit
// controls work fine.
//
RedrawWindow(ped->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME);
}
break;
default:
PassToDefaultWindowProc:
return DefWindowProc(ped->hwnd, message, wParam, lParam);
}
return TRUE;
}
//---------------------------------------------------------------------------//
//
// EditML_DrawText AorW
//
// This function draws all the characters between ichStart and ichEnd for
// the given Multiline Edit Control.
//
// This function divides the block of text between ichStart and ichEnd
// into lines and each line into strips of text based on the selection
// attributes. It calls Edit_TabTheTextOut() to draw each strip.
// This takes care of the Negative A anc C widths of the current font, if
// it has any, on either side of each strip of text.
//
// NOTE: If the language pack is loaded the text is not divided into strips,
// nor is selection highlighting performed here. Whole lines are passed
// to the language pack to display with tab expansion and selection
// highlighting. (Since the language pack supports scripts with complex
// character re-ordering rules, only it can do this).
//
VOID EditML_DrawText(PED ped, HDC hdc, ICH ichStart, ICH ichEnd, BOOL fSelChange)
{
DWORD textColorSave;
DWORD bkColorSave;
PSTR pText;
UINT wCurLine;
UINT wEndLine;
INT xOffset;
ICH LengthToDraw;
ICH CurStripLength;
ICH ichAttrib, ichNewStart;
ICH ExtraLengthForNegA;
ICH ichT;
INT iRemainingLengthInLine;
INT xStPos, xClipStPos, xClipEndPos, yPos;
BOOL fFirstLineOfBlock = TRUE;
BOOL fDrawEndOfLineStrip = FALSE;
BOOL fDrawOnSameLine = FALSE;
BOOL fSelected = FALSE;
BOOL fLineBegins = FALSE;
STRIPINFO NegCInfo;
POINT pt;
HBRUSH hbr = NULL;
BOOL fNeedDelete = FALSE;
//
// Just return if nothing to draw
//
if (!ped->ichLinesOnScreen)
{
return;
}
hbr = Edit_GetBrush(ped, hdc, &fNeedDelete);
if (hbr && fNeedDelete)
{
DeleteObject(hbr);
}
//
// Adjust the value of ichStart such that we need to draw only those lines
// visible on the screen.
//
if ((UINT)ichStart < (UINT)ped->chLines[ped->ichScreenStart])
{
ichStart = ped->chLines[ped->ichScreenStart];
if (ichStart > ichEnd)
{
return;
}
}
//
// Adjust the value of ichEnd such that we need to draw only those lines
// visible on the screen.
//
wCurLine = min(ped->ichScreenStart+ped->ichLinesOnScreen,ped->cLines-1);
ichT = ped->chLines[wCurLine] + EditML_Line(ped, wCurLine);
ichEnd = min(ichEnd, ichT);
wCurLine = EditML_IchToLine(ped, ichStart); // Starting line.
wEndLine = EditML_IchToLine(ped, ichEnd); // Ending line.
UserAssert(ped->chLines[wCurLine] <= ped->cch + 1);
UserAssert(ped->chLines[wEndLine] <= ped->cch + 1);
if (fSelChange && (GetBkMode(hdc) != OPAQUE))
{
//
// if changing selection on a transparent edit control, just
// draw those lines from scratch
//
RECT rcStrip;
CopyRect(&rcStrip, &ped->rcFmt);
rcStrip.left -= ped->wLeftMargin;
if (ped->pLpkEditCallout)
{
rcStrip.right += ped->wRightMargin;
}
rcStrip.top += (wCurLine - ped->ichScreenStart) * ped->lineHeight;
rcStrip.bottom = rcStrip.top + ((wEndLine - wCurLine) + 1) * ped->lineHeight;
InvalidateRect(ped->hwnd, &rcStrip, TRUE);
return;
}
//
// If it is either centered or right-justified, then draw the whole lines.
// Also draw whole lines if the language pack is handling line layout.
//
if ((ped->format != ES_LEFT) || (ped->pLpkEditCallout))
{
ichStart = ped->chLines[wCurLine];
ichEnd = ped->chLines[wEndLine] + EditML_Line(ped, wEndLine);
}
pText = Edit_Lock(ped);
HideCaret(ped->hwnd);
//
// If ichStart stays on Second byte of DBCS, we have to
// adjust it. LiZ -- 5/5/93
//
if (ped->fAnsi && ped->fDBCS)
{
ichStart = Edit_AdjustIch( ped, pText, ichStart );
}
UserAssert(ichStart <= ped->cch);
UserAssert(ichEnd <= ped->cch);
while (ichStart <= ichEnd)
{
//
// Pass whole lines to the language pack to display with selection
// marking and tab expansion.
//
if (ped->pLpkEditCallout)
{
ped->pLpkEditCallout->EditDrawText(
(PED0)ped, hdc, pText + ped->cbChar*ichStart,
EditML_Line(ped, wCurLine),
(INT)ped->ichMinSel - (INT)ichStart, (INT)ped->ichMaxSel - (INT)ichStart,
EditML_IchToYPos(ped, ichStart, FALSE));
}
else
{
//
// xStPos: The starting Position where the string must be drawn.
// xClipStPos: The starting position for the clipping rect for the block.
// xClipEndPos: The ending position for the clipping rect for the block.
//
//
// Calculate the xyPos of starting point of the block.
//
EditML_IchToXYPos(ped, hdc, ichStart, FALSE, &pt);
xClipStPos = xStPos = pt.x;
yPos = pt.y;
//
// The attributes of the block is the same as that of ichStart.
//
ichAttrib = ichStart;
//
// If the current font has some negative C widths and if this is the
// begining of a block, we must start drawing some characters before the
// block to account for the negative C widths of the strip before the
// current strip; In this case, reset ichStart and xStPos.
//
if (fFirstLineOfBlock && ped->wMaxNegC)
{
fFirstLineOfBlock = FALSE;
ichNewStart = max(((int)(ichStart - ped->wMaxNegCcharPos)), ((int)ped->chLines[wCurLine]));
//
// If ichStart needs to be changed, then change xStPos also accordingly.
//
if (ichNewStart != ichStart)
{
if (ped->fAnsi && ped->fDBCS)
{
//
// Adjust DBCS alignment...
//
ichNewStart = Edit_AdjustIchNext( ped, pText, ichNewStart );
}
EditML_IchToXYPos(ped, hdc, ichStart = ichNewStart, FALSE, &pt);
xStPos = pt.x;
}
}
//
// Calc the number of characters remaining to be drawn in the current line.
//
iRemainingLengthInLine = EditML_Line(ped, wCurLine) -
(ichStart - ped->chLines[wCurLine]);
//
// If this is the last line of a block, we may not have to draw all the
// remaining lines; We must draw only upto ichEnd.
//
if (wCurLine == wEndLine)
{
LengthToDraw = ichEnd - ichStart;
}
else
{
LengthToDraw = iRemainingLengthInLine;
}
//
// Find out how many pixels we indent the line for non-left-justified
// formats
//
if (ped->format != ES_LEFT)
{
xOffset = EditML_CalcXOffset(ped, hdc, wCurLine);
}
else
{
xOffset = -((int)(ped->xOffset));
}
//
// Check if this is the begining of a line.
//
if (ichAttrib == ped->chLines[wCurLine])
{
fLineBegins = TRUE;
xClipStPos = ped->rcFmt.left - ped->wLeftMargin;
}
//
// The following loop divides this 'wCurLine' into strips based on the
// selection attributes and draw them strip by strip.
//
do
{
//
// If ichStart is pointing at CRLF or CRCRLF, then iRemainingLength
// could have become negative because MLLine does not include
// CR and LF at the end of a line.
//
if (iRemainingLengthInLine < 0) // If Current line is completed,
{
break; // go on to the next line.
}
//
// Check if a part of the block is selected and if we need to
// show it with a different attribute.
//
if (!(ped->ichMinSel == ped->ichMaxSel ||
ichAttrib >= ped->ichMaxSel ||
ichEnd < ped->ichMinSel ||
(!ped->fNoHideSel && !ped->fFocus)))
{
//
// OK! There is a selection somewhere in this block!
// Check if this strip has selection attribute.
//
if (ichAttrib < ped->ichMinSel)
{
fSelected = FALSE; // This strip is not selected
// Calculate the length of this strip with normal attribute.
CurStripLength = min(ichStart+LengthToDraw, ped->ichMinSel)-ichStart;
fLineBegins = FALSE;
}
else
{
//
// The current strip has the selection attribute.
//
if (fLineBegins) // Is it the first part of a line?
{
//
// Then, draw the left margin area with normal attribute.
//
fSelected = FALSE;
CurStripLength = 0;
xClipStPos = ped->rcFmt.left - ped->wLeftMargin;
fLineBegins = FALSE;
}
else
{
//
// Else, draw the strip with selection attribute.
//
fSelected = TRUE;
CurStripLength = min(ichStart+LengthToDraw, ped->ichMaxSel)-ichStart;
//
// Select in the highlight colors.
//
bkColorSave = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
if (!ped->fDisabled)
{
textColorSave = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
}
}
}
else
{
//
// The whole strip has no selection attributes.
//
CurStripLength = LengthToDraw;
}
//
// Other than the current strip, do we still have anything
// left to be drawn in the current line?
//
fDrawOnSameLine = (LengthToDraw != CurStripLength);
//
// When we draw this strip, we need to draw some more characters
// beyond the end of this strip to account for the negative A
// widths of the characters that follow this strip.
//
ExtraLengthForNegA = min(iRemainingLengthInLine-CurStripLength, ped->wMaxNegAcharPos);
//
// The blank strip at the end of the line needs to be drawn with
// normal attribute irrespective of whether the line has selection
// attribute or not. Hence, if the last strip of the line has selection
// attribute, then this blank strip needs to be drawn separately.
// Else, we can draw the blank strip along with the last strip.
//
//
// Is this the last strip of the current line?
//
if (iRemainingLengthInLine == (int)CurStripLength)
{
if (fSelected) // Does this strip have selection attribute?
{
//
// Then we need to draw the end of line strip separately.
//
fDrawEndOfLineStrip = TRUE; // Draw the end of line strip.
EditML_IchToXYPos(ped, hdc, ichStart+CurStripLength, TRUE, &pt);
xClipEndPos = pt.x;
}
else
{
//
// Set the xClipEndPos to a big value sothat the blank
// strip will be drawn automatically when the last strip
// is drawn.
//
xClipEndPos = MAXCLIPENDPOS;
}
}
else
{
//
// This is not the last strip of this line; So, set the ending
// clip position accurately.
//
EditML_IchToXYPos(ped, hdc, ichStart+CurStripLength, FALSE, &pt);
xClipEndPos = pt.x;
}
//
// Draw the current strip starting from xStPos, clipped to the area
// between xClipStPos and xClipEndPos. Obtain "NegCInfo" and use it
// in drawing the next strip.
//
Edit_TabTheTextOut(hdc, xClipStPos, xClipEndPos,
xStPos, yPos, (LPSTR)(pText+ichStart*ped->cbChar),
CurStripLength+ExtraLengthForNegA, ichStart, ped,
ped->rcFmt.left+xOffset, fSelected ? ECT_SELECTED : ECT_NORMAL, &NegCInfo);
if (fSelected)
{
//
// If this strip was selected, then the next strip won't have
// selection attribute
//
fSelected = FALSE;
SetBkColor(hdc, bkColorSave);
if (!ped->fDisabled)
{
SetTextColor(hdc, textColorSave);
}
}
//
// Do we have one more strip to draw on the current line?
//
if (fDrawOnSameLine || fDrawEndOfLineStrip)
{
int iLastDrawnLength;
//
// Next strip's attribute is decided based on the char at ichAttrib
//
ichAttrib = ichStart + CurStripLength;
//
// When drawing the next strip, start at a few chars before
// the actual start to account for the Neg 'C' of the strip
// just drawn.
//
iLastDrawnLength = CurStripLength +ExtraLengthForNegA - NegCInfo.nCount;
//
// Adjust DBCS alignment...
//
if (ped->fAnsi && ped->fDBCS)
{
ichNewStart = Edit_AdjustIch(ped,pText,ichStart+iLastDrawnLength);
iLastDrawnLength = ichNewStart - ichStart;
ichStart = ichNewStart;
}
else
{
ichStart += iLastDrawnLength;
}
LengthToDraw -= iLastDrawnLength;
iRemainingLengthInLine -= iLastDrawnLength;
//
// The start of clip rect for the next strip.
//
xStPos = NegCInfo.XStartPos;
xClipStPos = xClipEndPos;
}
//
// Draw the blank strip at the end of line seperately, if required.
//
if (fDrawEndOfLineStrip)
{
Edit_TabTheTextOut(hdc, xClipStPos, MAXCLIPENDPOS, xStPos, yPos,
(LPSTR)(pText+ichStart*ped->cbChar), LengthToDraw, ichStart,
ped, ped->rcFmt.left+xOffset, ECT_NORMAL, &NegCInfo);
fDrawEndOfLineStrip = FALSE;
}
}
while(fDrawOnSameLine); // do while loop ends here.
}
// Let us move on to the next line of this block to be drawn.
wCurLine++;
if (ped->cLines > wCurLine)
{
ichStart = ped->chLines[wCurLine];
}
else
{
ichStart = ichEnd+1; // We have reached the end of the text.
}
} // while loop ends here
Edit_Unlock(ped);
ShowCaret(ped->hwnd);
EditML_SetCaretPosition(ped, hdc);
}
//---------------------------------------------------------------------------//
//
// Multi-Line Support Routines called Rarely
//---------------------------------------------------------------------------//
//
// EditML_InsertCrCrLf AorW
//
// Inserts CR CR LF characters into the text at soft (word-wrap) line
// breaks. CR LF (hard) line breaks are unaffected. Assumes that the text
// has already been formatted ie. ped->chLines is where we want the line
// breaks to occur. Note that ped->chLines is not updated to reflect the
// movement of text by the addition of CR CR LFs. Returns TRUE if successful
// else notify parent and return FALSE if the memory couldn't be allocated.
//
BOOL EditML_InsertCrCrLf(PED ped)
{
ICH dch;
ICH li;
ICH lineSize;
unsigned char *pchText;
unsigned char *pchTextNew;
if (!ped->fWrap || !ped->cch)
{
//
// There are no soft line breaks if word-wrapping is off or if no chars
//
return TRUE;
}
//
// Calc an upper bound on the number of additional characters we will be
// adding to the text when we insert CR CR LFs.
//
dch = 3 * ped->cLines;
if (!LocalReAlloc(ped->hText, (ped->cch + dch) * ped->cbChar, 0))
{
Edit_NotifyParent(ped, EN_ERRSPACE);
return FALSE;
}
ped->cchAlloc = ped->cch + dch;
//
// Move the text up dch bytes and then copy it back down, inserting the CR
// CR LF's as necessary.
//
pchTextNew = pchText = Edit_Lock(ped);
pchText += dch * ped->cbChar;
//
// We will use dch to keep track of how many chars we add to the text
//
dch = 0;
//
// Copy the text up dch bytes to pchText. This will shift all indices in
// ped->chLines up by dch bytes.
//
memmove(pchText, pchTextNew, ped->cch * ped->cbChar);
//
// Now copy chars from pchText down to pchTextNew and insert CRCRLF at soft
// line breaks.
//
if (ped->fAnsi)
{
for (li = 0; li < ped->cLines - 1; li++)
{
lineSize = ped->chLines[li + 1] - ped->chLines[li];
memmove(pchTextNew, pchText, lineSize);
pchTextNew += lineSize;
pchText += lineSize;
//
// If last character in newly copied line is not a line feed, then we
// need to add the CR CR LF triple to the end
//
if (*(pchTextNew - 1) != 0x0A)
{
*pchTextNew++ = 0x0D;
*pchTextNew++ = 0x0D;
*pchTextNew++ = 0x0A;
dch += 3;
}
}
//
// Now move the last line up. It won't have any line breaks in it...
//
memmove(pchTextNew, pchText, ped->cch - ped->chLines[ped->cLines - 1]);
}
else
{
LPWSTR pwchTextNew = (LPWSTR)pchTextNew;
for (li = 0; li < ped->cLines - 1; li++)
{
lineSize = ped->chLines[li + 1] - ped->chLines[li];
memmove(pwchTextNew, pchText, lineSize * sizeof(WCHAR));
pwchTextNew += lineSize;
pchText += lineSize * sizeof(WCHAR);
//
// If last character in newly copied line is not a line feed, then we
// need to add the CR CR LF triple to the end
//
if (*(pwchTextNew - 1) != 0x0A)
{
*pwchTextNew++ = 0x0D;
*pwchTextNew++ = 0x0D;
*pwchTextNew++ = 0x0A;
dch += 3;
}
}
//
// Now move the last line up. It won't have any line breaks in it...
//
memmove(pwchTextNew, pchText,
(ped->cch - ped->chLines[ped->cLines - 1]) * sizeof(WCHAR));
}
Edit_Unlock(ped);
if (dch)
{
//
// Update number of characters in text handle
//
ped->cch += dch;
//
// So that the next time we do anything with the text, we can strip the
// CRCRLFs
//
ped->fStripCRCRLF = TRUE;
return TRUE;
}
return FALSE;
}
//---------------------------------------------------------------------------//
//
// EditML_StripCrCrLf AorW
//
// Strips the CR CR LF character combination from the text. This
// shows the soft (word wrapped) line breaks. CR LF (hard) line breaks are
// unaffected.
//
void EditML_StripCrCrLf(PED ped)
{
if (ped->cch)
{
if (ped->fAnsi)
{
unsigned char *pchSrc;
unsigned char *pchDst;
unsigned char *pchLast;
pchSrc = pchDst = Edit_Lock(ped);
pchLast = pchSrc + ped->cch;
while (pchSrc < pchLast)
{
if ( (pchSrc[0] == 0x0D)
&& (pchSrc[1] == 0x0D)
&& (pchSrc[2] == 0x0A)
)
{
pchSrc += 3;
ped->cch -= 3;
}
else
{
*pchDst++ = *pchSrc++;
}
}
}
else
{
LPWSTR pwchSrc;
LPWSTR pwchDst;
LPWSTR pwchLast;
pwchSrc = pwchDst = (LPWSTR)Edit_Lock(ped);
pwchLast = pwchSrc + ped->cch;
while (pwchSrc < pwchLast)
{
if ( (pwchSrc[0] == 0x0D)
&& (pwchSrc[1] == 0x0D)
&& (pwchSrc[2] == 0x0A)
)
{
pwchSrc += 3;
ped->cch -= 3;
}
else
{
*pwchDst++ = *pwchSrc++;
}
}
}
Edit_Unlock(ped);
//
// Make sure we don't have any values past the last character
//
if (ped->ichCaret > ped->cch)
{
ped->ichCaret = ped->cch;
}
if (ped->ichMinSel > ped->cch)
{
ped->ichMinSel = ped->cch;
}
if (ped->ichMaxSel > ped->cch)
{
ped->ichMaxSel = ped->cch;
}
}
}
//---------------------------------------------------------------------------//
//
// EditML_SetHandle AorW
//
// Sets the ped to contain the given handle.
//
void EditML_SetHandle(PED ped, HANDLE hNewText)
{
ICH newCch;
ped->cch = ped->cchAlloc =
(ICH)LocalSize(ped->hText = hNewText) / ped->cbChar;
ped->fEncoded = FALSE;
if (ped->cch)
{
//
// We have to do it this way in case the app gives us a zero size handle
//
if (ped->fAnsi)
{
ped->cch = strlen(Edit_Lock(ped));
}
else
{
ped->cch = wcslen((LPWSTR)Edit_Lock(ped));
}
Edit_Unlock(ped);
}
newCch = (ICH)(ped->cch + CCHALLOCEXTRA);
//
// We do this LocalReAlloc in case the app changed the size of the handle
//
if (LocalReAlloc(ped->hText, newCch*ped->cbChar, 0))
{
ped->cchAlloc = newCch;
}
Edit_ResetTextInfo(ped);
}
//---------------------------------------------------------------------------//
//
// EditML_GetLine AorW
//
// Copies maxCchToCopy bytes of line lineNumber to the buffer
// lpBuffer. The string is not zero terminated.
//
// Returns number of characters copied
//
LONG EditML_GetLine(PED ped, ICH lineNumber, ICH maxCchToCopy, LPSTR lpBuffer)
{
PSTR pText;
ICH cchLen;
if (lineNumber > ped->cLines - 1)
{
TraceMsg(TF_STANDARD,
"Edit: Invalid parameter \"lineNumber\" (%ld) to EditML_GetLine",
lineNumber);
return 0L;
}
cchLen = EditML_Line(ped, lineNumber);
maxCchToCopy = min(cchLen, maxCchToCopy);
if (maxCchToCopy)
{
pText = Edit_Lock(ped) +
ped->chLines[lineNumber] * ped->cbChar;
memmove(lpBuffer, pText, maxCchToCopy*ped->cbChar);
Edit_Unlock(ped);
}
return maxCchToCopy;
}
//---------------------------------------------------------------------------//
//
// EditML_LineIndex AorW
//
// This function return s the number of character positions that occur
// preceeding the first char in a given line.
//
ICH EditML_LineIndex( PED ped, ICH iLine)
{
if (iLine == -1)
{
iLine = ped->iCaretLine;
}
if (iLine < ped->cLines)
{
return ped->chLines[iLine];
}
else
{
TraceMsg(TF_STANDARD,
"Edit: Invalid parameter \"iLine\" (%ld) to EditML_LineIndex",
iLine);
return (ICH)-1;
}
}
//---------------------------------------------------------------------------//
//
// EditML_LineLength AorW
//
// if ich = -1, return the length of the lines containing the current
// selection but not including the selection. Otherwise, return the length of
// the line containing ich.
//
ICH EditML_LineLength(PED ped, ICH ich)
{
ICH il1, il2;
ICH temp;
if (ich != 0xFFFFFFFF)
{
return (EditML_Line(ped, EditML_IchToLine(ped, ich)));
}
//
// Find length of lines corresponding to current selection
//
il1 = EditML_IchToLine(ped, ped->ichMinSel);
il2 = EditML_IchToLine(ped, ped->ichMaxSel);
if (il1 == il2)
{
return (EditML_Line(ped, il1) - (ped->ichMaxSel - ped->ichMinSel));
}
temp = ped->ichMinSel - ped->chLines[il1];
temp += EditML_Line(ped, il2);
temp -= (ped->ichMaxSel - ped->chLines[il2]);
return temp;
}
//---------------------------------------------------------------------------//
//
// EditML_SetSelection AorW
//
// Sets the selection to the points given and puts the cursor at
// ichMaxSel.
//
VOID EditML_SetSelection(PED ped, BOOL fDoNotScrollCaret, ICH ichMinSel, ICH ichMaxSel)
{
HDC hdc;
if (ichMinSel == 0xFFFFFFFF)
{
//
// Set no selection if we specify -1
//
ichMinSel = ichMaxSel = ped->ichCaret;
}
//
// Since these are unsigned, we don't check if they are greater than 0.
//
ichMinSel = min(ped->cch, ichMinSel);
ichMaxSel = min(ped->cch, ichMaxSel);
#ifdef FE_SB // EditML_SetSelectionHander()
//
// 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
// at first. We're worrying about the overhead of Edit_Lock and Edit_Unlock.
//
if ( ped->fDBCS && ped->fAnsi )
{
PSTR pText;
pText = Edit_Lock(ped);
ichMinSel = Edit_AdjustIch( ped, pText, ichMinSel );
ichMaxSel = Edit_AdjustIch( ped, pText, ichMaxSel );
Edit_Unlock(ped);
}
#endif // FE_SB
//
// Set the caret's position to be at ichMaxSel.
//
ped->ichCaret = ichMaxSel;
ped->iCaretLine = EditML_IchToLine(ped, ped->ichCaret);
hdc = Edit_GetDC(ped, FALSE);
EditML_ChangeSelection(ped, hdc, ichMinSel, ichMaxSel);
EditML_SetCaretPosition(ped, hdc);
Edit_ReleaseDC(ped, hdc, FALSE);
#ifdef FE_SB // EditML_SetSelectionHander()
if (!fDoNotScrollCaret)
{
EditML_EnsureCaretVisible(ped);
}
//
// #ifdef KOREA is history, with FE_SB (FarEast Single Binary).
//
#else
#ifdef KOREA
//
// Extra parameter specified interim character mode
//
EditML_EnsureCaretVisible(ped,NULL);
#else
if (!fDoNotScrollCaret)
{
EditML_EnsureCaretVisible(ped);
}
#endif
#endif // FE_SB
}
//---------------------------------------------------------------------------//
//
// EditML_SetTabStops AorW
//
// This sets the tab stop positions set by the App by sending
// a EM_SETTABSTOPS message.
//
// nTabPos : Number of tab stops set by the caller
// lpTabStops: array of tab stop positions in Dialog units.
//
// Returns:
// TRUE if successful
// FALSE if memory allocation error.
//
BOOL EditML_SetTabStops(PED ped, int nTabPos, LPINT lpTabStops)
{
int *pTabStops;
//
// Check if tab positions already exist
//
if (!ped->pTabStops)
{
//
// Check if the caller wants the new tab positions
//
if (nTabPos)
{
//
// Allocate the array of tab stops
//
pTabStops = (LPINT)UserLocalAlloc(HEAP_ZERO_MEMORY, (nTabPos + 1) * sizeof(int));
if (!pTabStops)
{
return FALSE;
}
}
else
{
//
// No stops then and no stops now!
//
return TRUE;
}
}
else
{
//
// Check if the caller wants the new tab positions
//
if (nTabPos)
{
//
// Check if the number of tab positions is different
//
if (ped->pTabStops[0] != nTabPos)
{
//
// Yes! So ReAlloc to new size
//
pTabStops = (LPINT)UserLocalReAlloc(ped->pTabStops, (nTabPos + 1) * sizeof(int), 0);
if (!pTabStops)
{
return FALSE;
}
}
else
{
pTabStops = ped->pTabStops;
}
}
else
{
//
// Caller wants to remove all the tab stops; So, release
//
if (!UserLocalFree(ped->pTabStops))
{
return FALSE;
}
ped->pTabStops = NULL;
goto RedrawAndReturn;
}
}
//
// Copy the new tab stops onto the tab stop array after converting the
// dialog co-ordinates into the pixel co-ordinates
//
ped->pTabStops = pTabStops;
//
// First element contains the count
//
*pTabStops++ = nTabPos;
while (nTabPos--)
{
//
// aveCharWidth must be used instead of cxSysCharWidth.
// Fix for Bug #3871 --SANKAR-- 03/14/91
//
*pTabStops++ = MultDiv(*lpTabStops++, ped->aveCharWidth, 4);
}
RedrawAndReturn:
//
// Because the tabstops have changed, we need to recompute the
// maxPixelWidth. Otherwise, horizontal scrolls will have problems.
// Fix for Bug #6042 - 3/15/94
//
EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
//
// Caret may have changed line by the line recalc above.
//
EditML_UpdateiCaretLine(ped);
EditML_EnsureCaretVisible(ped);
//
// Also, we need to redraw the whole window.
//
InvalidateRect(ped->hwnd, NULL, TRUE);
return TRUE;
}
//---------------------------------------------------------------------------//
//
// EditML_Undo AorW
//
// Handles Undo for multiline edit controls.
//
BOOL EditML_Undo(PED ped)
{
HANDLE hDeletedText = ped->hDeletedText;
BOOL fDelete = (BOOL)(ped->undoType & UNDO_DELETE);
ICH cchDeleted = ped->cchDeleted;
ICH ichDeleted = ped->ichDeleted;
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
//
EditML_SetSelection(ped, FALSE, ped->ichInsStart, ped->ichInsEnd);
ped->ichInsStart = ped->ichInsEnd = (ICH)-1;
//
// Now send a backspace to delete and save it in the undo buffer...
//
SendMessage(ped->hwnd, WM_CHAR, (WPARAM)VK_BACK, 0L);
}
if (fDelete)
{
//
// Insert deleted chars
//
//
// Set the selection to the inserted text
//
EditML_SetSelection(ped, FALSE, ichDeleted, ichDeleted);
EditML_InsertText(ped, hDeletedText, cchDeleted, FALSE);
GlobalFree(hDeletedText);
EditML_SetSelection(ped, FALSE, ichDeleted, ichDeleted + cchDeleted);
}
return TRUE;
}
//---------------------------------------------------------------------------//
//
// EditML_Create AorW
//
// 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 EditML_Create(PED ped, LPCREATESTRUCT lpCreateStruct)
{
LONG windowStyle;
LPWSTR lpszName;
//
// Get values from the window instance data structure and put them in the
// ped so that we can access them easier
//
windowStyle = GET_STYLE(ped);
//
// Do the standard creation stuff
//
if (!Edit_Create(ped, windowStyle))
{
return -1;
}
//
// Allocate line start array in local heap and lock it down
//
ped->chLines = (LPICH)LocalAlloc(LPTR, 2 * sizeof(int));
if (ped->chLines == NULL)
{
return -1;
}
//
// Call it one line of text...
//
ped->cLines = 1;
//
// If app wants WS_VSCROLL or WS_HSCROLL, it automatically gets AutoVScroll
// or AutoHScroll.
//
if ((windowStyle & ES_AUTOVSCROLL) || (windowStyle & WS_VSCROLL))
{
ped->fAutoVScroll = 1;
}
if (ped->format != ES_LEFT)
{
//
// If user wants right or center justified text, then we turn off
// AUTOHSCROLL and WS_HSCROLL since non-left styles don't make sense
// otherwise.
//
windowStyle &= ~WS_HSCROLL;
ClearWindowState(ped->hwnd, WS_HSCROLL);
ped->fAutoHScroll = FALSE;
}
else if (windowStyle & WS_HSCROLL)
{
ped->fAutoHScroll = TRUE;
}
ped->fWrap = (!ped->fAutoHScroll && !(windowStyle & WS_HSCROLL));
//
// Max # chars we will allow user to enter
//
ped->cchTextMax = MAXTEXT;
//
// 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
// unitialized. Fail to create the control.
return -1;
}
//
// Set the window text if needed and notify parent if not enough memory to
// set the initial text.
//
#if 0
if ((ULONG_PTR)lpCreateStruct->lpszName > gHighestUserAddress)
{
lpszName = REBASEPTR(ped->pwnd, (PVOID)lpCreateStruct->lpszName);
}
else
#endif
{
lpszName = (LPWSTR)lpCreateStruct->lpszName;
}
if (!Edit_SetEditText(ped, (LPSTR)lpszName))
{
return -1;
}
return TRUE;
}