|
|
/*++
* * WOW v1.0 * * Copyright (c) 1991, Microsoft Corporation * * EDITML.C * Win16 edit control code * * History: * * Created 28-May-1991 by Jeff Parsons (jeffpar) * Copied from WIN31 and edited (as little as possible) for WOW16. --*/
/****************************************************************************/ /* editml.c - Edit controls rewrite. Version II of edit controls. */ /* */ /* */ /* Created: 24-Jul-88 davidds */ /****************************************************************************/
#define NO_LOCALOBJ_TAGS
#include "user.h"
#include "edit.h"
/****************************************************************************/ /* Multi-Line Support Routines */ /****************************************************************************/
BOOL NEAR _fastcall MLIsDelimiter(char bCharVal) { return((bCharVal == ' ') || (bCharVal == '\t')); }
DWORD NEAR PASCAL LocGetTabbedTextExtent(HDC hdc, PSTR pstring, int nCount, PED ped) { return(ECTabTheTextOut(hdc, 0, 0, (LPSTR)pstring, nCount, ped, 0, FALSE)); }
int FAR PASCAL MLCalcXOffset(register PED ped, HDC hdc, int lineNumber) /* effects: Calculates the horizontal offset (indent) required for centered
* and right justified lines. */ { PSTR pText; ICH lineLength; register ICH lineWidth;
if (ped->format == ES_LEFT) return(0);
lineLength = MLLineLength(ped, lineNumber);
if (lineLength) { pText = LocalLock(ped->hText)+ped->chLines[lineNumber]; hdc = ECGetEditDC(ped,TRUE); lineWidth = LOWORD(LocGetTabbedTextExtent(hdc, pText, lineLength, ped)); ECReleaseEditDC(ped,hdc,TRUE); LocalUnlock(ped->hText); } else lineWidth = 0;
/*
* If a SPACE or a TAB was eaten at the end of a line by MLBuildchLines * 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, 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, lineWidth-1)); }
ICH NEAR PASCAL MLMoveSelection(register PED ped, ICH ich, BOOL fLeft) /* effects: 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... */ { register PSTR pText;
if (fLeft && ich > 0) { /* Move left */ #ifdef FE_SB
pText = LMHtoP( ped->hText ) + ich; if( ECIsDBCSLeadByte(ped, *ECAnsiPrev(ped, LMHtoP(ped->hText), pText ) ) ) ich--; #endif /* FE_SB */
ich--; if (ich) { /* Check for CRLF or CRCRLF */ /*pText = LocalLock(ped->hText)+ich;*/ pText = LMHtoP(ped->hText)+ich;
/* Move before CRLF or CRCRLF */ if (*pText == 0x0A) { ich--; if (ich && *(pText-2) == 0x0D) ich--; } /*LocalUnlock(ped->hText);*/ } } else if (!fLeft && ich < ped->cch) { #ifdef FE_SB
pText = LMHtoP(ped->hText)+ich; if( ECIsDBCSLeadByte(ped, *pText ) ) ich++; #endif /* FE_SB */
ich++; if (ich < ped->cch) { /*pText = LocalLock(ped->hText)+ich;*/ pText = LMHtoP(ped->hText)+ich;
/* Move after CRLF */ if (*pText == 0x0A) ich++; else /* Check for CRCRLF */ if (ich && *pText == 0x0D && *(pText-1) == 0x0D) ich+=2; /*LocalUnlock(ped->hText);*/ } } return(ich); }
void FAR PASCAL MLSetCaretPosition(register PED ped, HDC hdc) /* effects: If the window has the focus, find where the caret belongs and move
* it there. */ { LONG position; BOOL prevLine;
/* 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 || ped->fNoRedraw) return;
if(ped->fCaretHidden) { SetCaretPos(-20000, -20000); return; }
/* Find the position of the caret */ if ((ped->iCaretLine < ped->screenStart) || (ped->iCaretLine > ped->screenStart+ped->ichLinesOnScreen)) /* Caret is not visible. Make it a very small value so that it won't be
* seen. */ SetCaretPos(-20000,-20000); else { if (ped->cLines-1 != ped->iCaretLine && ped->ichCaret == ped->chLines[ped->iCaretLine+1]) prevLine=TRUE; else prevLine=FALSE;
position = MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine); if (ped->fWrap) { if (HIWORD(position) > ped->rcFmt.bottom-ped->lineHeight) SetCaretPos(-20000,-20000); else { /* 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. */ SetCaretPos(min(LOWORD(position),ped->rcFmt.right-1), HIWORD(position)); } } else { if (LOWORD(position) > ped->rcFmt.right || HIWORD(position) > ped->rcFmt.bottom) SetCaretPos(-20000,-20000); else SetCaretPos(LOWORD(position),HIWORD(position)); } } }
ICH FAR PASCAL MLLineLength(register PED ped, int lineNumber) /* effects: Returns the length of the line given by lineNumber ignoring any
* CRLFs in the line. */ { ICH result; register PSTR pText;
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]; /* Now check for CRLF or CRCRLF at end of line */ if (result > 1) { /*pText = LocalLock(ped->hText)+ped->chLines[lineNumber+1]-2;*/ pText = LMHtoP(ped->hText)+ped->chLines[lineNumber+1]-2; if (*pText == 0x0D) { result = result - 2; if (result && *(--pText) == 0x0D) /* In case there was a CRCRLF */ result--; } /*LocalUnlock(ped->hText);*/ } } return(result); }
int FAR PASCAL MLIchToLineHandler(register PED ped, ICH ich) /* effects: Returns the line number (starting from 0) which contains the given
* character index. If ich is -1, return the line the the first char in the * selection is on (the caret if no selection) */ { register int line = ped->cLines-1;
if (ich == 0xFFFF) ich = ped->ichMinSel;
/* We could do a binary search here but is it really worth it??? We will
* have to wait and see how often this proc is being called... */
while (line && (ich < ped->chLines[line])) line--;
return(line); }
LONG NEAR PASCAL MLIchToXYPos(register PED ped, HDC hdc, ICH ich, BOOL prevLine) /* effects: 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). x is in the loword, * y in the highword. */ { 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 = MLIchToLineHandler(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->screenStart)*ped->lineHeight+ped->rcFmt.top;
/* Now determine the xPosition of the character */ pTextStart = LocalLock(ped->hText);
if (prevLine && iline && (ich == ped->chLines[iline]) && (*(pTextStart+ich-1) != 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];
/* Note that we are taking the position in front of any CRLFs in the
* text. */ cch = MLLineLength(ped, iline); } else { pLineStart = pTextStart + ped->chLines[iline]; pText = pTextStart + ich;
/* 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 (ich && *pText == 0x0A) pText--; if (ich > 2 && *(pText-1) == 0x0D) pText--; }
cch = pText - pLineStart; }
/* Find out how many pixels we indent the line for funny formats */ if (ped->format != ES_LEFT) xOffset = MLCalcXOffset(ped, hdc, iline); else xOffset = -ped->xOffset;
xPosition = ped->rcFmt.left + xOffset + LOWORD(LocGetTabbedTextExtent(hdc, pLineStart, cch, ped));
LocalUnlock(ped->hText); return(MAKELONG(xPosition,yPosition)); }
LONG NEAR PASCAL MLMouseToIch(register PED ped, HDC hdc, POINT mousePt) /* effects: Returns the closest cch to where the mouse point is. cch is in
* the lowword and lineindex is in the high word. (So that we can tell if we * are at the beginning of the line or end of the previous line.) */ { int xOffset; PSTR pText; PSTR pLineStart; int height = mousePt.y; int line; int width = mousePt.x; ICH cch; ICH cLineLength; ICH cLineLengthNew; ICH cLineLengthHigh=0; ICH cLineLengthLow=0; int textWidth; int iOldTextWidth; int iCurWidth;
/* First determine which line the mouse is pointing to.
*/ line = ped->screenStart; 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+ped->ichLinesOnScreen,ped->cLines-1); else /* We are somewhere on a line visible on screen */ line=min(line+((height-ped->rcFmt.top)/ped->lineHeight),ped->cLines-1);
/* Now determine what horizontal character the mouse is pointing to.
*/ pLineStart=(pText=LocalLock(ped->hText))+ped->chLines[line]; cLineLength = MLLineLength(ped,line); /* Length is sans CRLF or CRCRLF */
/* 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 = MLCalcXOffset(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 = ECCchInWidth(ped, hdc, (LPSTR)pLineStart, cLineLength, ped->rcFmt.right-ped->rcFmt.left+ped->xOffset); #ifdef FE_SB
{ ICH cch2; cch2 = umin(cch+1,cLineLength); if (ECAdjustIch( ped, pLineStart, cch2 ) != cch2) /* Displayed character on the right edge is DBCS */ cch = umin(cch+2,cLineLength); else cch = cch2; cch += ped->chLines[line]; } #else
cch = ped->chLines[line]+umin(cch+1,cLineLength); #endif
} 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 = ECCchInWidth(ped, hdc, (LPSTR)pLineStart, cLineLength, ped->xOffset); if (cch) cch--;
#ifdef FE_SB
cch = ECAdjustIch( ped, pLineStart, cch ); #endif
cch = ped->chLines[line]+cch; } else { /* Now the mouse is somewhere on the visible portion of the text
* remember cch contains the length the line. */ iCurWidth = width + ped->xOffset;
cLineLengthHigh = cLineLength+1; while(cLineLengthLow < cLineLengthHigh-1) { cLineLengthNew = umax((cLineLengthHigh-cLineLengthLow)/2,1)+ cLineLengthLow;
/* Add in a avecharwidth/2 so that if user is half way on the next
* char, it is still considered the previous char. For that feel. */ textWidth = ped->rcFmt.left + ped->aveCharWidth/2 + LOWORD(LocGetTabbedTextExtent(hdc, pLineStart, cLineLengthNew, ped)); if (textWidth > iCurWidth) cLineLengthHigh = cLineLengthNew; else cLineLengthLow = cLineLengthNew;
/* Preserve the old Width */ iOldTextWidth = textWidth; }
/* Find out which side of the character the mouse click occurred */ if ((iOldTextWidth - iCurWidth) < (iCurWidth - textWidth)) cLineLengthNew++;
cLineLength = umin(cLineLengthNew,cLineLength);
#ifdef FE_SB
cLineLength = ECAdjustIch( ped, pLineStart, cLineLength ); #endif
cch = ped->chLines[line]+cLineLength; } LocalUnlock(ped->hText); return(MAKELONG(cch,line)); }
void NEAR PASCAL MLRepaintChangedSelection(PED ped, HDC hdc, ICH ichOldMinSel, ICH ichOldMaxSel)
/* When selection changes, this takes care of drawing the changed portions
* with proper attributes. */ { BLOCK Blk[2]; int iBlkCount = 0; int i;
Blk[0].StPos = ichOldMinSel; Blk[0].EndPos = ichOldMaxSel; Blk[1].StPos = ped->ichMinSel; Blk[1].EndPos = ped->ichMaxSel;
if (ECCalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel, (LPBLOCK)&Blk[0], (LPBLOCK)&Blk[1])) { /* Paint the rectangles where selection has changed */ /* Paint both Blk[0] and Blk[1], if they exist */ for(i = 0; i < 2; i++) { if (Blk[i].StPos != -1) MLDrawText(ped, hdc, Blk[i].StPos, Blk[i].EndPos); } } }
void FAR PASCAL MLChangeSelection(register PED ped, HDC hdc, ICH ichNewMinSel, ICH ichNewMaxSel) /* effects: 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. */ {
ICH temp; ICH ichOldMinSel, ichOldMaxSel;
if (ichNewMinSel > ichNewMaxSel) { temp = ichNewMinSel; ichNewMinSel = ichNewMaxSel; ichNewMaxSel = temp; } ichNewMinSel = umin(ichNewMinSel, ped->cch); ichNewMaxSel = umin(ichNewMaxSel, ped->cch);
/* Save the current selection */ ichOldMinSel = ped->ichMinSel; ichOldMaxSel = ped->ichMaxSel;
/* Set new selection */ ped->ichMinSel = ichNewMinSel; ped->ichMaxSel = ichNewMaxSel;
/* We only update the selection on the screen if redraw is on and if we have
* the focus or if we don't hide the selection when we don't have the focus. */ if (!ped->fNoRedraw && (ped->fFocus || ped->fNoHideSel)) { /* Find old selection region, find new region, and invert the XOR of the
* two and invert only the XOR region. */ MLRepaintChangedSelection(ped, hdc, ichOldMinSel, ichOldMaxSel);
MLSetCaretPosition(ped,hdc); }
}
// 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 NEAR PASCAL MLUpdateiCaretLine(PED ped) { PSTR pText;
ped->iCaretLine = MLIchToLineHandler(ped, ped->ichCaret);
/* If caret gets to beginning of next line, pop it up to end of current line
* when inserting text. */ pText = LMHtoP(ped->hText)+ped->ichCaret-1; if (ped->iCaretLine && ped->chLines[ped->iCaretLine] == ped->ichCaret && *pText != 0x0A) ped->iCaretLine--; }
ICH FAR PASCAL MLInsertText(ped, lpText, cchInsert, fUserTyping) register PED ped; LPSTR lpText; ICH cchInsert; BOOL fUserTyping; /* effects: 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 returned (could be 0). If we can't allocate the required * space, we notify the parent with EN_ERRSPACE and no characters are added. * We will rebuild the lines array as needed. fUserTyping is true if the * input was the result of the user typing at the keyboard. This is so we can * do some stuff faster since we will be getting only one or two chars of * input. */ { HDC hdc; ICH validCch=cchInsert; ICH oldCaret = ped->ichCaret; int oldCaretLine = ped->iCaretLine; BOOL fCRLF=FALSE; LONG l; WORD localundoType = 0; HANDLE localhDeletedText; ICH localichDeleted; ICH localcchDeleted; ICH localichInsStart; ICH localichInsEnd; LONG xyPosInitial=0L; LONG xyPosFinal=0L;
if (!validCch) return(0);
if (validCch) { /* Limit the amount of text we add */ _asm int 3 if (ped->cchTextMax <= ped->cch) { /* When the max chars is reached already, notify parent */ /* Fix for Bug #4183 -- 02/06/91 -- SANKAR -- */ ECNotifyParent(ped,EN_MAXTEXT); return(0); }
validCch = umin(validCch, ped->cchTextMax - ped->cch); /* Make sure we don't split a CRLF in half */ if (validCch && *(lpText+validCch) == 0x0A) validCch--; if (!validCch) { /* When the max chars is reached already, notify parent */ /* Fix for Bug #4183 -- 02/06/91 -- SANKAR -- */ ECNotifyParent(ped,EN_MAXTEXT); return(0); }
if (validCch == 2 && (*(WORD FAR *)lpText) == 0x0A0D) fCRLF=TRUE;
if (!ped->fAutoVScroll && (ped->undoType==UNDO_INSERT || ped->undoType==UNDO_DELETE)) { /* Save off the current undo state */ localundoType = ped->undoType; localhDeletedText = ped->hDeletedText; localichDeleted = ped->ichDeleted; localcchDeleted = ped->cchDeleted; localichInsStart = ped->ichInsStart; localichInsEnd = ped->ichInsEnd;
/* Kill undo */ ped->undoType = UNDO_NONE; ped->hDeletedText = NULL; ped->ichDeleted= ped->cchDeleted = ped->ichInsStart= ped->ichInsEnd = 0; }
hdc = ECGetEditDC(ped,FALSE); if (ped->cch) xyPosInitial = MLIchToXYPos(ped, hdc, ped->cch-1, FALSE);
/* Insert the text */ if (!ECInsertText(ped, lpText, validCch)) { ECReleaseEditDC(ped,hdc,FALSE); ECNotifyParent(ped, EN_ERRSPACE); return(0); } /* Note that ped->ichCaret is updated by ECInsertText */ }
l = MLBuildchLines(ped, oldCaretLine, validCch, (fCRLF ? FALSE : fUserTyping));
if (ped->cch) xyPosFinal = MLIchToXYPos(ped, hdc, ped->cch-1, FALSE);
if (HIWORD(xyPosFinal) < HIWORD(xyPosInitial) && ped->screenStart+ped->ichLinesOnScreen >= ped->cLines-1) { RECT rc; CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt); rc.top = HIWORD(xyPosFinal)+ped->lineHeight; InvalidateRect(ped->hwnd, (LPRECT)&rc, TRUE); }
if (!ped->fAutoVScroll) { if (ped->ichLinesOnScreen < ped->cLines) { MLUndoHandler(ped); ECEmptyUndo(ped); if (localundoType == UNDO_INSERT || localundoType == UNDO_DELETE) { ped->undoType = localundoType; ped->hDeletedText = localhDeletedText; ped->ichDeleted = localichDeleted; ped->cchDeleted = localcchDeleted; ped->ichInsStart = localichInsStart; ped->ichInsEnd = localichInsEnd; } MessageBeep(0); ECReleaseEditDC(ped,hdc,FALSE); return(0); } else { if (localundoType == UNDO_INSERT || localundoType == UNDO_DELETE) GlobalFree(localhDeletedText); } }
if (fUserTyping && ped->fWrap) #ifdef FE_SB
/* To avoid oldCaret points intermediate of DBCS character,
* adjust oldCaret position if necessary. */ oldCaret = ECAdjustIch(ped, LMHtoP(ped->hText), umin(LOWORD(l), oldCaret)); #else
oldCaret = umin(LOWORD(l), oldCaret); #endif
/* These are updated by ECInsertText so we won't do it again */ /* ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;*/ #ifdef NEVER
ped->iCaretLine = MLIchToLineHandler(ped, ped->ichCaret);
/* If caret gets to beginning of next line, pop it up to end of current line
* when inserting text. */ pText = LMHtoP(ped->hText)+ped->ichCaret-1; if (ped->iCaretLine && ped->chLines[ped->iCaretLine] == ped->ichCaret && *pText != 0x0A) ped->iCaretLine--; #else
// Update ped->iCaretLine properly.
MLUpdateiCaretLine(ped); #endif
ECNotifyParent(ped,EN_UPDATE);
if (fCRLF || !fUserTyping) /* Redraw to end of screen/text if crlf or large insert.
*/ MLDrawText(ped, hdc, (fUserTyping ? oldCaret : 0), ped->cch); else MLDrawText(ped, hdc, oldCaret, umax(ped->ichCaret,HIWORD(l)));
ECReleaseEditDC(ped,hdc,FALSE);
/* Make sure we can see the cursor */ MLEnsureCaretVisible(ped);
ped->fDirty = TRUE;
ECNotifyParent(ped,EN_CHANGE);
if (validCch < cchInsert) ECNotifyParent(ped,EN_MAXTEXT);
return(validCch); }
ICH NEAR PASCAL MLDeleteText(register PED ped) /* effects: Deletes the characters between ichMin and ichMax. Returns the
* number of characters we deleted. */ { ICH minSel = ped->ichMinSel; ICH maxSel = ped->ichMaxSel; ICH cchDelete; HDC hdc; int minSelLine; int maxSelLine; LONG xyPos; RECT rc; BOOL fFastDelete = FALSE; LONG l; #ifdef FE_SB
ICH cchcount; #endif
/* Get what line the min selection is on so that we can start rebuilding the
* text from there if we delete anything. */ minSelLine = MLIchToLineHandler(ped,minSel); maxSelLine = MLIchToLineHandler(ped,maxSel); #ifdef FE_SB
switch(maxSel - minSel) { case 2: if (ECIsDBCSLeadByte(ped,*(LMHtoP(ped->hText)+minSel)) == FALSE) break; /* Fall thru */ case 1: // if ((minSelLine==maxSelLine) && (ped->chLines[minSelLine] != minSel))
if (minSelLine==maxSelLine) { if (!ped->fAutoVScroll) fFastDelete = FALSE; else { fFastDelete = TRUE; cchcount = ((maxSel - minSel) == 1) ? 0 : -1; } } } #else
if (((maxSel - minSel) == 1) && (minSelLine==maxSelLine) && (ped->chLines[minSelLine] != minSel)) { if (!ped->fAutoVScroll) fFastDelete = FALSE; else fFastDelete = TRUE; } #endif
if (!(cchDelete=ECDeleteText(ped))) return(0);
/* Start building lines at minsel line since caretline may be at the max sel
* point. */ if (fFastDelete) { #ifdef FE_SB
MLShiftchLines(ped, minSelLine+1, -2+cchcount); #else
MLShiftchLines(ped, minSelLine+1, -2); #endif
l=MLBuildchLines(ped, minSelLine, 1, TRUE); } else { MLBuildchLines(ped,max(minSelLine-1,0),-cchDelete, FALSE); } #ifdef NEVER
ped->iCaretLine = MLIchToLineHandler(ped, ped->ichCaret);
/* If caret gets to beginning of this line, pop it up to end of previous
* line when deleting text */ pText = LMHtoP(ped->hText)+ped->ichCaret; if (ped->iCaretLine && ped->chLines[ped->iCaretLine] == ped->ichCaret && *(pText-1)!= 0x0A) ped->iCaretLine--; #else
MLUpdateiCaretLine(ped); #endif
#if 0
if (!ped->fAutoVScroll) ECEmptyUndo(ped); #endif
ECNotifyParent(ped,EN_UPDATE);
/* Now update the screen to reflect the deletion */ hdc = ECGetEditDC(ped,FALSE); /* Otherwise just redraw starting at the line we just entered */ minSelLine = max(minSelLine-1,0); if (fFastDelete) MLDrawText(ped, hdc, ped->chLines[minSelLine], HIWORD(l)); else MLDrawText(ped, hdc, ped->chLines[minSelLine], ped->cch);
if (ped->cch) { /* Clear from end of text to end of window.
*/ xyPos = MLIchToXYPos(ped, hdc, ped->cch, FALSE); CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt); rc.top = HIWORD(xyPos)+ped->lineHeight; InvalidateRect(ped->hwnd, (LPRECT)&rc, TRUE); } else { InvalidateRect(ped->hwnd, (LPRECT)&ped->rcFmt, TRUE); } ECReleaseEditDC(ped,hdc,FALSE);
MLEnsureCaretVisible(ped);
ped->fDirty = TRUE;
ECNotifyParent(ped,EN_CHANGE); return(cchDelete); }
BOOL NEAR PASCAL MLInsertchLine(register PED ped, int iLine, ICH ich, BOOL fUserTyping) /* effects: 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. */ { HANDLE hResult;
if (fUserTyping && iLine < ped->cLines) { ped->chLines[iLine] = ich; return(TRUE); }
hResult = LocalReAlloc((HANDLE)ped->chLines,(ped->cLines+2)*sizeof(int),LHND);
if (!hResult) { ECNotifyParent(ped, EN_ERRSPACE); return(FALSE); }
ped->chLines = (int *)hResult; /* Move indices starting at iLine up */ LCopyStruct((LPSTR)(&ped->chLines[iLine]),(LPSTR)(&ped->chLines[iLine+1]), (ped->cLines-iLine)*sizeof(int)); ped->cLines++;
ped->chLines[iLine] = ich; return(TRUE); }
#if 0
BOOL NEAR PASCAL MLGrowLinesArray(ped, cLines) register PED ped; int cLines; /* effects: Grows the line start array in the ped so that it holds cLines.
* Notifies parent and returns FALSE if no memory error. We won't allow more * than 32700 lines in the edit control. This allows us to use signed values * for line counts while still providing good functionality. */ { HANDLE hResult;
if (cLines<32700 && (hResult = LocalReAlloc((HANDLE)ped->chLines, (cLines+1)*sizeof(ICH),LHND)))
ped->chLines = (int *)hResult; else ECNotifyParent(ped, EN_ERRSPACE);
return((BOOL)hResult); } #endif
void NEAR PASCAL MLShiftchLines(register PED ped, register int iLine, int delta) /* effects: Move the starting index of all lines iLine or greater by delta
* bytes. */ { if (iLine >= ped->cLines) return;
/* Just add delta to the starting point of each line after iLine */ for (;iLine<ped->cLines;iLine++) ped->chLines[iLine] += delta; }
LONG FAR PASCAL MLBuildchLines(ped, iLine, cchDelta, fUserTyping) register PED ped; int iLine; int cchDelta; BOOL fUserTyping; /* effects: Rebuilds the start of line array (ped->chLines) starting at line
* number ichLine. Returns TRUE if any new lines were made else returns * false. */ { register 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;
int iLineStart = iLine; ICH cLineLength; ICH cch; HDC hdc;
BOOL fLineBroken = FALSE; /* Initially, no new line breaks are made */ ICH minCchBreak; ICH maxCchBreak;
if (!ped->cch) { ped->maxPixelWidth = 0; ped->xOffset = 0; ped->screenStart = 0; ped->cLines = 1; return(TRUE); }
if (fUserTyping && cchDelta) MLShiftchLines(ped, iLine+1, cchDelta);
hdc = ECGetEditDC(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 = LocalLock(ped->hText);
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.
*/ while (ichCRLF < ped->cch && *(ptext+ichCRLF) != 0x0D) ichCRLF++; }
if (!ped->fWrap) { /* If we are not word wrapping, line breaks are signified by CRLF.
*/
/* We will limit lines to MAXLINELENGTH characters maximum */ ichLineEnd=ichLineStart + umin(ichCRLF-ichLineStart,MAXLINELENGTH);
#ifdef FE_SB
/* To avoid separating between DBCS char, adjust character
* position to DBCS lead byte if necessary. */ ichLineEnd = ECAdjustIch(ped, ptext, ichLineEnd); #endif
/* We will keep track of what the longest line is for the horizontal
* scroll bar thumb positioning. */ ped->maxPixelWidth = umax(ped->maxPixelWidth, (unsigned int) LOWORD(LocGetTabbedTextExtent(hdc, (ptext+ichLineStart), ichLineEnd-ichLineStart, ped))); } else { /* Find the end of the line based solely on text extents */ ichLineEnd = ichLineStart + ECCchInWidth(ped, hdc, (LPSTR)(ptext+ichLineStart), ichCRLF-ichLineStart, ped->rcFmt.right-ped->rcFmt.left); if (ichLineEnd == ichLineStart && ichCRLF-ichLineStart) { /* Maintain a minimum of one char per line */ ichLineEnd++; }
#ifdef NEVER
/* Now starting from ichLineEnd, if we are not at a hard line break,
* then if we are not at a space (or CR) or the char before us is * not a space, we will look word left for the start of the word to * break at. */ if (ichLineEnd != ichCRLF) if (!MLIsDelimiter(*(ptext+ichLineEnd)) || *(ptext+ichLineEnd) == 0x0D || !MLIsDelimiter(*(ptext+ichLineEnd-1))) #else
/* 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 ((!MLIsDelimiter(*(ptext+ichLineEnd)) && !MLIsDelimiter(*(ptext+ichLineEnd-1))) || *(ptext+ichLineEnd) == 0x0D) #endif
{ #ifdef FE_SB
cch = LOWORD(ECWord(ped, ichLineEnd, FALSE)); #else
cch = LOWORD(ECWord(ped, ichLineEnd, TRUE)); #endif
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 (!MLIsDelimiter((*(ptext+ichLineEnd-1))) && MLIsDelimiter((*(ptext+ichLineEnd)))) #endif
if ((*(ptext+ichLineEnd-1)!= ' ' && *(ptext+ichLineEnd-1)!= TAB) && (*(ptext+ichLineEnd) == ' ' || *(ptext+ichLineEnd) == TAB)) /* Swallow the space at the end of a line. */ ichLineEnd++;
/* Skip over crlf or crcrlf if it exists. Thus, ichLineEnd is the first
* character in the next line. */ ichLineEndBeforeCRLF = ichLineEnd;
if (*(ptext+ichLineEnd) == 0x0D) ichLineEnd +=2; /* Skip over CRCRLF */ if (*(ptext+ichLineEnd) == 0x0A) ichLineEnd++;
/* Now, increment iLine, allocate space for the next line, and set its
* starting point */ iLine++;
if (!fUserTyping ||/* (iLineStart+1 >= iLine) || */ (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 = umax(maxCchBreak,ichLineEnd);
LocalUnlock(ped->hText); /* Now insert the new line into the array */ if (!MLInsertchLine(ped, iLine, ichLineEnd, (BOOL)(cchDelta!=0))) { ECReleaseEditDC(ped,hdc,TRUE); return(MAKELONG(minCchBreak,maxCchBreak)); }
ptext = LocalLock(ped->hText); } else { maxCchBreak = ped->chLines[iLine]; /* Quick escape */ goto EndUp; }
ichLineStart = ichLineEnd; } /* end while (ichLineStart < ped->cch) */
if (iLine != ped->cLines) { 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 && *(ptext+ped->cch-1) == 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= umax(maxCchBreak,ichLineEnd); LocalUnlock(ped->hText); MLInsertchLine(ped, iLine, ped->cch, FALSE); } else EndUp: LocalUnlock(ped->hText);
ECReleaseEditDC(ped, hdc, TRUE); return(MAKELONG(minCchBreak,maxCchBreak)); }
void NEAR PASCAL MLPaintHandler(register PED ped, HDC althdc) /* effects: Handles WM_PAINT messages.
*/ { PAINTSTRUCT ps; HDC hdc; HDC hdcWindow; RECT rcEdit; DWORD dwStyle; HANDLE hOldFont; POINT pt; ICH imin; ICH imax;
/* Allow subclassed hdcs.
*/ if (althdc) hdc = althdc; else hdc = BeginPaint(ped->hwnd, (PAINTSTRUCT FAR *)&ps);
if (!ped->fNoRedraw && IsWindowVisible(ped->hwnd)) { #if 0
if (althdc || ps.fErase) { hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT); /* Erase the background since we don't do it in the erasebkgnd
* message */ FillWindow(ped->hwndParent, ped->hwnd, hdc, hBrush); } #endif
if (ped->fBorder) { // hdcWindow = GetWindowDC(ped->hwnd);
hdcWindow = hdc; GetWindowRect(ped->hwnd, &rcEdit); OffsetRect(&rcEdit, -rcEdit.left, -rcEdit.top);
dwStyle = GetWindowLong(ped->hwnd, GWL_STYLE); if (HIWORD(dwStyle) & HIWORD(WS_SIZEBOX)) { /* Note we can't use user's globals here since we are on a
* different ds when in the edit control code. */ InflateRect(&rcEdit, -GetSystemMetrics(SM_CXFRAME)+ GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYFRAME)+ GetSystemMetrics(SM_CYBORDER));
}
DrawFrame(hdcWindow, (LPRECT)&rcEdit, 1, DF_WINDOWFRAME); // ReleaseDC(ped->hwnd, hdcWindow);
}
ECSetEditClip(ped, hdc); if (ped->hFont) hOldFont = SelectObject(hdc, ped->hFont);
if (!althdc) { pt.x = ps.rcPaint.left; pt.y = ps.rcPaint.top; imin = MLMouseToIch(ped, hdc, pt)-1; if (imin == -1) imin = 0; pt.x = ps.rcPaint.right; pt.y = ps.rcPaint.bottom; imax = MLMouseToIch(ped, hdc, pt)+1; MLDrawText(ped, hdc, imin, imax); } else MLDrawText(ped, hdc, 0, ped->cch);
if (ped->hFont && hOldFont) SelectObject(hdc, hOldFont); }
if (!althdc) EndPaint(ped->hwnd, (PAINTSTRUCT FAR *)&ps); }
void FAR PASCAL MLCharHandler(ped, keyValue, keyMods) register PED ped; WORD keyValue; int keyMods; /* effects: Handles character input (really, no foolin')
*/ { char unsigned keyPress = LOBYTE(keyValue); BOOL updateText = FALSE; int scState; #ifdef FE_SB
WORD DBCSkey; #endif
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;
if (!keyMods) { /* Get state of modifier keys for use later. */ scState = ((GetKeyState(VK_CONTROL) & 0x8000) ? 1 : 0); /* We are just interested in state of the ctrl key */ /* scState += ((GetKeyState(VK_SHIFT) & 0x8000) ? 2 : 0); */ } else scState = ((keyMods == NOMODIFY) ? 0 : keyMods);
if (ped->fInDialogBox && ((keyPress == VK_TAB && scState != CTRLDOWN) || keyPress == VK_RETURN)) /* 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. */ return;
if (ped->fReadOnly) /* Ignore keys in read only controls.
*/ return;
if (keyPress == 0x0A) keyPress = VK_RETURN;
if (keyPress == VK_TAB || keyPress == VK_RETURN || keyPress == VK_BACK || keyPress >= ' ') /* Delete the selected text if any */ if (MLDeleteText(ped)) updateText=TRUE;
#ifdef FE_SB
if( IsDBCSLeadByte( keyPress ) ) if( ( DBCSkey = DBCSCombine( ped->hwnd, keyPress ) ) != NULL ) keyValue = DBCSkey; #endif
switch(keyPress) { 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 = MLMoveSelection(ped,ped->ichCaret,TRUE); MLDeleteText(ped); } break;
default: if (keyPress == VK_RETURN) keyValue = 0x0A0D;
if (keyPress >= ' ' || keyPress == VK_RETURN || keyPress == VK_TAB) MLInsertText(ped, (LPSTR) &keyValue, HIBYTE(keyValue) ? 2 : 1, TRUE); else MessageBeep(0);
break; } }
void NEAR PASCAL MLKeyDownHandler(register PED ped, WORD virtKeyCode, int keyMods) /* effects: Handles cursor movement and other VIRT KEY stuff. keyMods allows
* us to make MLKeyDownHandler 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. */ { HDC hdc;
/* Variables we will use for redrawing the updated text */ register ICH newMaxSel = ped->ichMaxSel; register ICH newMinSel = ped->ichMinSel;
/* Flags for drawing the updated text */ BOOL changeSelection = FALSE; /* new selection is specified by
newMinSel, newMaxSel */
/* Comparisons we do often */ BOOL MinEqMax = (newMaxSel == newMinSel); BOOL MinEqCar = (ped->ichCaret == newMinSel); BOOL MaxEqCar = (ped->ichCaret == newMaxSel);
/* State of shift and control keys. */ int scState = 0;
long position; BOOL prevLine; POINT mousePt; int defaultDlgId; int iScrollAmt;
if (ped->fMouseDown) /* If we are in the middle of a mousedown command, don't do anything.
*/ return;
if (!keyMods) { /* Get state of modifier keys for use later. */ scState = ((GetKeyState(VK_CONTROL) & 0x8000) ? 1 : 0); scState += ((GetKeyState(VK_SHIFT) & 0x8000) ? 2 : 0); } else scState = ((keyMods == NOMODIFY) ? 0 : 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 MLCharHandler, we handle this as if a return was * entered. */ if (scState != CTRLDOWN) { defaultDlgId = LOWORD(SendMessage(ped->hwndParent, DM_GETDEFID, 0, 0L)); if (defaultDlgId) { defaultDlgId=(WORD)GetDlgItem(ped->hwndParent, defaultDlgId); if (defaultDlgId) { SendMessage(ped->hwndParent, WM_NEXTDLGCTL, defaultDlgId, 1L); if (!ped->fFocus) PostMessage((HWND)defaultDlgId, 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) MLCharHandler(ped, virtKeyCode, keyMods); else if (ped->fInDialogBox) SendMessage(ped->hwndParent, WM_NEXTDLGCTL, scState==SHFTDOWN, 0L); return; break;
case VK_LEFT: /* If the caret isn't already at 0, we can move left */ if (ped->ichCaret) { switch (scState) { case NONEDOWN: /* Clear selection, move caret left */ ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, TRUE); newMaxSel = newMinSel = ped->ichCaret; break;
case CTRLDOWN: /* Clear selection, move caret word left */ ped->ichCaret = LOWORD(ECWord(ped,ped->ichCaret,TRUE)); newMaxSel = newMinSel = ped->ichCaret; break;
case SHFTDOWN: /* Extend selection, move caret left */ ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, TRUE); if (MaxEqCar && !MinEqMax) /* Reduce selection extent */ newMaxSel = ped->ichCaret; else /* Extend selection extent */ newMinSel = ped->ichCaret; break;
case SHCTDOWN: /* Extend selection, move caret word left */ ped->ichCaret = LOWORD(ECWord(ped,ped->ichCaret,TRUE)); if (MaxEqCar && !MinEqMax) { /* Reduce selection extent */ /* Hint: Suppose WORD. OR is selected. Cursor between
R and D. Hit select word left, we want to just select the W and leave cursor before the W. */ newMinSel = ped->ichMinSel; newMaxSel = ped->ichCaret; } else /* Extend selection extent */ newMinSel = ped->ichCaret; break; } 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 == NONEDOWN || scState == CTRLDOWN)) { changeSelection = TRUE; newMaxSel = newMinSel = ped->ichCaret; } } break;
case VK_RIGHT: /* If the caret isn't already at ped->cch, we can move right */ if (ped->ichCaret < ped->cch) { switch (scState) { case NONEDOWN: /* Clear selection, move caret right */ ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, FALSE); newMaxSel = newMinSel = ped->ichCaret; break;
case CTRLDOWN: /* Clear selection, move caret word right */ ped->ichCaret = HIWORD(ECWord(ped,ped->ichCaret,FALSE)); newMaxSel = newMinSel = ped->ichCaret; break;
case SHFTDOWN: /* Extend selection, move caret right */ ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, FALSE); if (MinEqCar && !MinEqMax) /* Reduce selection extent */ newMinSel = ped->ichCaret; else /* Extend selection extent */ newMaxSel = ped->ichCaret; break;
case SHCTDOWN: /* Extend selection, move caret word right */ ped->ichCaret = HIWORD(ECWord(ped,ped->ichCaret,FALSE)); if (MinEqCar && !MinEqMax) { /* Reduce selection extent */ newMinSel = ped->ichCaret; newMaxSel = ped->ichMaxSel; } else /* Extend selection extent */ newMaxSel = ped->ichCaret; break; }
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 == NONEDOWN || scState == CTRLDOWN)) { newMaxSel = newMinSel = ped->ichCaret; changeSelection = TRUE; } } break;
case VK_UP: case VK_DOWN: if (virtKeyCode == VK_UP && ped->cLines-1 != ped->iCaretLine && ped->ichCaret == ped->chLines[ped->iCaretLine+1]) prevLine= TRUE; else prevLine=FALSE; hdc = ECGetEditDC(ped,TRUE); position = MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine); ECReleaseEditDC(ped, hdc, TRUE); mousePt.x = LOWORD(position); mousePt.y = HIWORD(position)+1+ (virtKeyCode == VK_UP ? -ped->lineHeight : ped->lineHeight);
if (scState == NONEDOWN || scState == SHFTDOWN) { /* Send fake mouse messages to handle this */ /* NONEDOWN: Clear selection, move caret up/down 1 line */ /* SHFTDOWN: Extend selection, move caret up/down 1 line */ MLMouseMotionHandler(ped, WM_LBUTTONDOWN, scState==NONEDOWN ? 0 : MK_SHIFT, mousePt); MLMouseMotionHandler(ped, WM_LBUTTONUP, scState==NONEDOWN ? 0 : MK_SHIFT, mousePt); } break;
case VK_HOME: switch (scState) { case NONEDOWN: /* Clear selection, move cursor to beginning of line */ newMaxSel = newMinSel = ped->ichCaret = ped->chLines[ped->iCaretLine]; break;
case CTRLDOWN: /* Clear selection, move caret to beginning of text */ newMaxSel = newMinSel = ped->ichCaret = 0; break;
case SHFTDOWN: /* Extend selection, move caret to beginning of line */ ped->ichCaret = ped->chLines[ped->iCaretLine]; if (MaxEqCar && !MinEqMax) { /* Reduce selection extent */ newMinSel = ped->ichMinSel; newMaxSel = ped->ichCaret; } else /* Extend selection extent */ newMinSel = ped->ichCaret; break;
case SHCTDOWN: /* Extend selection, move caret to beginning of text */ ped->ichCaret = newMinSel = 0; if (MaxEqCar && !MinEqMax) /* Reduce/negate selection extent */ newMaxSel = ped->ichMinSel; break; }
changeSelection = TRUE; break;
case VK_END: switch (scState) { case NONEDOWN: /* Clear selection, move caret to end of line */ newMaxSel = newMinSel = ped->ichCaret = ped->chLines[ped->iCaretLine]+MLLineLength(ped, ped->iCaretLine); break;
case CTRLDOWN: /* Clear selection, move caret to end of text */ newMaxSel = newMinSel = ped->ichCaret = ped->cch; break;
case SHFTDOWN: /* Extend selection, move caret to end of line */ ped->ichCaret = ped->chLines[ped->iCaretLine]+ MLLineLength(ped, ped->iCaretLine); if (MinEqCar && !MinEqMax) { /* Reduce selection extent */ newMinSel = ped->ichCaret; newMaxSel = ped->ichMaxSel; } else /* Extend selection extent */ newMaxSel = ped->ichCaret; break;
case SHCTDOWN: newMaxSel = ped->ichCaret = ped->cch; /* Extend selection, move caret to end of text */ if (MinEqCar && !MinEqMax) /* Reduce/negate selection extent */ newMinSel = ped->ichMaxSel; /* else Extend selection extent */ break; }
changeSelection = TRUE; break;
case VK_PRIOR: case VK_NEXT:
switch (scState) { case NONEDOWN: case SHFTDOWN: /* Vertical scroll by one visual screen */ hdc = ECGetEditDC(ped,TRUE); position = MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine); ECReleaseEditDC(ped, hdc, TRUE); mousePt.x = LOWORD(position); mousePt.y = HIWORD(position)+1;
SendMessage(ped->hwnd, WM_VSCROLL, virtKeyCode == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN, 0L);
/* Move the cursor there */ MLMouseMotionHandler(ped, WM_LBUTTONDOWN, scState==NONEDOWN ? 0 : MK_SHIFT, mousePt); MLMouseMotionHandler(ped, WM_LBUTTONUP, scState==NONEDOWN ? 0 : MK_SHIFT, mousePt); break;
case CTRLDOWN: /* Horizontal scroll by one screenful minus one char */ iScrollAmt = ((ped->rcFmt.right - ped->rcFmt.left) /ped->aveCharWidth) - 1; if(virtKeyCode == VK_PRIOR) iScrollAmt *= -1; /* For previous page */
SendMessage(ped->hwnd, WM_HSCROLL, EM_LINESCROLL, (long)iScrollAmt);
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... */ ped->ichCaret = MLMoveSelection(ped, ped->ichCaret, FALSE); ped->ichMaxSel = ped->ichMinSel = ped->ichCaret; SendMessage(ped->hwnd, WM_CHAR, (WORD) BACKSPACE, 0L); } if (ped->ichMinSel != ped->ichMaxSel) SendMessage(ped->hwnd, WM_CHAR, (WORD) BACKSPACE, 0L); break;
case SHFTDOWN: /* CUT selection ie. remove and copy to clipboard, or if no
* selection, delete (clear) character left. */
if (SendMessage(ped->hwnd, WM_COPY, (WORD)0,0L) || (ped->ichMinSel == ped->ichMaxSel)) /* If copy successful, delete the copied text by sending a
* backspace message which will redraw the text and take care * of notifying the parent of changes. Or if there is no * selection, just delete char left. */ SendMessage(ped->hwnd, WM_CHAR, (WORD) BACKSPACE, 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]+ MLLineLength(ped, ped->iCaretLine); } if (ped->ichMinSel != ped->ichMaxSel) SendMessage(ped->hwnd, WM_CHAR, (WORD) BACKSPACE, 0L); break; } /*
* No need to update text or selection since BACKSPACE message does it * for us. */ break;
case VK_INSERT: if (scState == CTRLDOWN || (scState == SHFTDOWN && !ped->fReadOnly)) /* if CTRLDOWN Copy current selection to clipboard */ /* if SHFTDOWN Paste clipboard */ SendMessage(ped->hwnd, scState == CTRLDOWN ? WM_COPY : WM_PASTE, (WORD)NULL, (LONG)NULL); break;
}
if (changeSelection) { hdc = ECGetEditDC(ped,FALSE); MLChangeSelection(ped,hdc,newMinSel,newMaxSel); /* Set the caret's line */ ped->iCaretLine = MLIchToLineHandler(ped, ped->ichCaret); if (virtKeyCode == VK_END && ped->ichCaret < ped->cch && ped->fWrap && ped->iCaretLine > 0) { /* 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 (*((PSTR)LMHtoP(ped->hText)+ped->chLines[ped->iCaretLine]-2) != 0x0D) ped->iCaretLine--; } /* Since drawtext sets the caret position */ MLSetCaretPosition(ped,hdc); ECReleaseEditDC(ped,hdc,FALSE);
/* Make sure we can see the cursor */ MLEnsureCaretVisible(ped); }
}
ICH PASCAL NEAR MLPasteText(register PED ped) /* effects: 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. */ { HANDLE hData; LPSTR lpchClip; LPSTR lpchClip2; register ICH cchAdded=0; /* No added data initially */ DWORD clipboardDataSize; HCURSOR hCursorOld;
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. */ ECEmptyUndo(ped);
/* See if any text should be deleted */ MLDeleteText(ped);
hCursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
if (!OpenClipboard(ped->hwnd)) goto PasteExitNoCloseClip;
if (!(hData = GetClipboardData(CF_TEXT))) { goto PasteExit; }
if (GlobalFlags(hData) & GMEM_DISCARDED) { goto PasteExit; }
lpchClip2 = lpchClip = (LPSTR) GlobalLock(hData);
/* Assumes lstrlen returns at most 64K
*/ cchAdded = umin((WORD)lstrlen(lpchClip),64000L);
/* Insert the text (MLInsertText checks line length) */ cchAdded = MLInsertText(ped, lpchClip, cchAdded, FALSE);
GlobalUnlock(hData);
PasteExit: CloseClipboard();
PasteExitNoCloseClip: if (hCursorOld) SetCursor(hCursorOld);
return(cchAdded); }
void NEAR PASCAL MLMouseMotionHandler(register PED ped, WORD message, WORD virtKeyDown, POINT mousePt) { LONG selection; BOOL updateText = FALSE; BOOL changeSelection = FALSE;
HDC hdc = ECGetEditDC(ped,TRUE); ICH newMaxSel = ped->ichMaxSel; ICH newMinSel = ped->ichMinSel; ICH ichStart = ped->screenStart;
ICH mouseCch; ICH mouseLine; int i,j;
selection = MLMouseToIch(ped, hdc, mousePt); mouseCch = LOWORD(selection); mouseLine = HIWORD(selection);
/* 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. */ #ifdef FE_SB
/*
* if character on the caret is DBCS, word selection is only one * DBCS character on the caret (or right side from the caret). */ { PSTR pCaretChr = LocalLock(ped->hText)+ped->ichCaret; selection = ECWord(ped,ped->ichCaret, ECIsDBCSLeadByte(ped,*pCaretChr) ? FALSE : TRUE); LocalUnlock(ped->hText); } #else
selection = ECWord(ped,ped->ichCaret,TRUE); #endif
newMinSel = LOWORD(selection); newMaxSel = ped->ichCaret = HIWORD(selection);
ped->iCaretLine = MLIchToLineHandler(ped, ped->ichCaret);
changeSelection = TRUE; /*
* Set mouse down to false so that the caret isn't reposition on the * mouseup message or on a accidental move... */ ped->fMouseDown = FALSE; break;
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 = 400 - ((WORD)i << 4); if (j < 100) j = 100; SetTimer(ped->hwnd, 1, j, (FARPROC)NULL); changeSelection = TRUE; /* Extend selection, move caret right */ if ((ped->ichMinSel == ped->ichCaret) && (ped->ichMinSel != ped->ichMaxSel)) { /* Reduce selection extent */ newMinSel = ped->ichCaret = mouseCch; newMaxSel = ped->ichMaxSel; } else { /* Extend selection extent */ newMaxSel = ped->ichCaret=mouseCch; } ped->iCaretLine = mouseLine; } break;
case WM_LBUTTONDOWN: /* if (ped->fFocus)
{*/ /*
* Only handle this if we have the focus. */ ped->fMouseDown = TRUE; SetCapture(ped->hwnd); changeSelection = TRUE; if (!(virtKeyDown & MK_SHIFT)) { /*
* If shift key isn't down, move caret to mouse point and clear * old selection */ newMinSel = newMaxSel = ped->ichCaret = mouseCch; ped->iCaretLine = mouseLine; } else { /*
* Shiftkey is down so we want to maintain the current selection * (if any) and just extend or reduce it */ if (ped->ichMinSel == ped->ichCaret) newMinSel = ped->ichCaret = mouseCch; else newMaxSel = ped->ichCaret = mouseCch; ped->iCaretLine = mouseLine; }
/*
* Set the timer so that we can scroll automatically when the mouse * is moved outside the window rectangle. */ ped->ptPrevMouse=mousePt; ped->prevKeys = virtKeyDown; SetTimer(ped->hwnd, 1, 400, (FARPROC)NULL); /* }*/ break;
case WM_LBUTTONUP: if (ped->fMouseDown) { /* Kill the timer so that we don't do auto mouse moves anymore */ KillTimer(ped->hwnd, 1); ReleaseCapture(); MLSetCaretPosition(ped,hdc); ped->fMouseDown = FALSE; } break; }
if (changeSelection) { MLChangeSelection(ped, hdc, newMinSel, newMaxSel); MLEnsureCaretVisible(ped); }
ECReleaseEditDC(ped,hdc,TRUE);
if (!ped->fFocus && (message == WM_LBUTTONDOWN)) /* If we don't have the focus yet, get it */ SetFocus(ped->hwnd);
}
int NEAR PASCAL MLPixelFromCount(register PED ped, int dCharLine, WORD message) /* effects: Given a character or line count (depending on message == hscroll
* or vscroll), calculate the number of pixels we must scroll to get there. * Updates the start of screen or xoffset to reflect the new positions. */ { /* This can be an int since we can have 32K max lines/pixels
*/ int oldLineChar;
if (message != WM_HSCROLL) { /* We want to scroll screen by dCharLine lines */ oldLineChar = ped->screenStart;
/* Find the new starting line for the ped */ ped->screenStart = max(ped->screenStart+dCharLine,0); ped->screenStart = min(ped->screenStart,ped->cLines-1);
dCharLine = oldLineChar - ped->screenStart;
/* We will scroll at most a screen full of text */ if (dCharLine < 0) dCharLine = -min(-dCharLine, ped->ichLinesOnScreen); else dCharLine = min(dCharLine, ped->ichLinesOnScreen);
return(dCharLine*ped->lineHeight); }
/* No horizontal scrolling allowed if funny format */ if (ped->format != ES_LEFT) return(0);
/* Convert delta characters into delta pixels */ dCharLine = dCharLine*ped->aveCharWidth;
oldLineChar = ped->xOffset;
/* Find new horizontal starting point */ ped->xOffset = max(ped->xOffset+dCharLine,0); ped->xOffset = min(ped->xOffset,ped->maxPixelWidth);
dCharLine = oldLineChar - ped->xOffset;
/* We will scroll at most a screen full of text */ if (dCharLine < 0) dCharLine = -min(-dCharLine, ped->rcFmt.right+1-ped->rcFmt.left); else dCharLine = min(dCharLine, ped->rcFmt.right+1-ped->rcFmt.left);
return(dCharLine); }
int NEAR PASCAL MLPixelFromThumbPos(register PED ped, int pos, BOOL fVertical) /* effects: Given a thumb position from 0 to 100, return the number of pixels
* we must scroll to get there. */ { int dxy; int iLineOld; ICH iCharOld;
if (fVertical) { iLineOld = ped->screenStart; ped->screenStart = (int)MultDiv(ped->cLines-1, pos, 100); ped->screenStart = min(ped->screenStart,ped->cLines-1); dxy = (iLineOld - ped->screenStart)*ped->lineHeight; } else { /* Only allow horizontal scrolling with left justified text */ if (ped->format == ES_LEFT) { iCharOld = ped->xOffset; ped->xOffset = MultDiv(ped->maxPixelWidth-ped->aveCharWidth, pos, 100); dxy = iCharOld - ped->xOffset; } else dxy = 0; }
return(dxy); }
int FAR PASCAL MLThumbPosFromPed(register PED ped, BOOL fVertical) /*
* effects: Given the current state of the edit control, return its vertical * thumb position if fVertical else returns its horizontal thumb position. * The thumb position ranges from 0 to 100. */ { WORD d1; WORD d2;
if (fVertical) { if (ped->cLines < 2) return(0); d1 = (WORD)(ped->screenStart); d2 = (WORD)(ped->cLines-1); } else { if (ped->maxPixelWidth < (ped->aveCharWidth*2)) return(0); d1 = (WORD)(ped->xOffset); d2 = (WORD)(ped->maxPixelWidth); }
/* Do the multiply/division and avoid overflows and divide by zero errors */ return(MultDiv(d1, 100, d2)); }
LONG NEAR PASCAL MLScrollHandler(register PED ped, WORD message, int cmd, int iAmt) { RECT rc; RECT rcUpdate; int dx; int dy; int dcharline; BOOL fVertical; HDC hdc;
UpdateWindow(ped->hwnd);
/* Are we scrolling vertically or horizontally? */ fVertical = (message != WM_HSCROLL); dx = dy = dcharline = 0;
switch (cmd) { case SB_LINEDOWN: dcharline = 1; break;
case SB_LINEUP: dcharline = -1; break;
case SB_PAGEDOWN: dcharline = ped->ichLinesOnScreen-1; break;
case SB_PAGEUP: dcharline = -(ped->ichLinesOnScreen-1); break;
case SB_THUMBTRACK: case SB_THUMBPOSITION: dy = MLPixelFromThumbPos(ped, iAmt, fVertical); dcharline = -dy / (message == WM_VSCROLL ? ped->lineHeight : ped->aveCharWidth); if (!fVertical) { dx = dy; dy = 0; } break;
case EM_LINESCROLL: dcharline = iAmt; break;
case EM_GETTHUMB: return(MLThumbPosFromPed(ped,fVertical)); break;
default: return(0L); break; }
GetClientRect(ped->hwnd, (LPRECT)&rc); IntersectRect((LPRECT)&rc, (LPRECT)&rc, (LPRECT)&ped->rcFmt); rc.bottom++;
if (cmd != SB_THUMBPOSITION && cmd != SB_THUMBTRACK) { if (message == WM_VSCROLL) { dx = 0; dy = MLPixelFromCount(ped, dcharline, message); } else if (message == WM_HSCROLL) { dx = MLPixelFromCount(ped, dcharline, message); dy = 0; } }
SetScrollPos(ped->hwnd, fVertical ? SB_VERT : SB_HORZ, (int)MLThumbPosFromPed(ped,fVertical), TRUE);
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. For example * NOTEPAD is such a #@@!@#@ an app since they don't use editcontrol * scroll bars and depend on these EN_*SCROLL messages to update their * fake scroll bars. */ ECNotifyParent(ped,fVertical ? EN_VSCROLL : EN_HSCROLL);
if (!ped->fNoRedraw) { hdc = ECGetEditDC(ped,FALSE); ScrollDC(hdc,dx,dy, (LPRECT)&rc, (LPRECT)&rc, NULL, (LPRECT)&rcUpdate); MLSetCaretPosition(ped,hdc); ECReleaseEditDC(ped,hdc,FALSE);
if (ped->ichLinesOnScreen+ped->screenStart >= ped->cLines) { InvalidateRect(ped->hwnd, (LPRECT)&rcUpdate, TRUE); } else { InvalidateRect(ped->hwnd, (LPRECT)&rcUpdate, FALSE); } UpdateWindow(ped->hwnd); }
return(MAKELONG(dcharline, 1)); }
void NEAR PASCAL MLSetFocusHandler(register PED ped) /* effects: Gives the edit control the focus and notifies the parent
* EN_SETFOCUS. */ { HDC hdc;
if (!ped->fFocus) { ped->fFocus = 1; /* Set focus */
hdc = ECGetEditDC(ped,TRUE);
/* Draw the caret */ CreateCaret(ped->hwnd, (HBITMAP)NULL, 2, ped->lineHeight); ShowCaret(ped->hwnd); MLSetCaretPosition(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) MLDrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel);
ECReleaseEditDC(ped,hdc,TRUE); } #if 0
MLEnsureCaretVisible(ped); #endif
/* Notify parent we have the focus */ ECNotifyParent(ped, EN_SETFOCUS); }
void NEAR PASCAL MLKillFocusHandler(register PED ped) /* effects: The edit control loses the focus and notifies the parent via
* EN_KILLFOCUS. */ { HDC hdc;
if (ped->fFocus) { ped->fFocus = 0; /* Clear focus */
/* Do this only if we still have the focus. But we always notify the
* parent that we lost the focus whether or not we originally had the * focus. */ /* Hide the current selection if needed */ if (!ped->fNoHideSel && ped->ichMinSel!=ped->ichMaxSel) { hdc = ECGetEditDC(ped,FALSE); MLDrawText(ped, hdc, ped->ichMinSel, ped->ichMaxSel); ECReleaseEditDC(ped,hdc,FALSE); }
/* Destroy the caret */ HideCaret(ped->hwnd); DestroyCaret(); }
/* Notify parent that we lost the focus. */ ECNotifyParent(ped, EN_KILLFOCUS); }
BOOL FAR PASCAL MLEnsureCaretVisible(ped) register PED ped; /*
* effects: Scrolls the caret into the visible region. Returns TRUE if * scrolling was done else returns FALSE. */ { int cLinesToScroll; int iLineMax; int xposition; BOOL prevLine; register HDC hdc; BOOL fScrolled = FALSE;
if (IsWindowVisible(ped->hwnd)) { if (ped->fAutoVScroll) { iLineMax = ped->screenStart + ped->ichLinesOnScreen-1;
if (fScrolled = ped->iCaretLine > iLineMax) { MLScrollHandler(ped, WM_VSCROLL, EM_LINESCROLL, ped->iCaretLine-iLineMax); } else { if (fScrolled = ped->iCaretLine < ped->screenStart) MLScrollHandler(ped, WM_VSCROLL, EM_LINESCROLL, ped->iCaretLine-ped->screenStart); } }
if (ped->fAutoHScroll && ped->maxPixelWidth > ped->rcFmt.right-ped->rcFmt.left) { /* Get the current position of the caret in pixels */ if (ped->cLines-1 != ped->iCaretLine && ped->ichCaret == ped->chLines[ped->iCaretLine+1]) prevLine=TRUE; else prevLine=FALSE;
hdc = ECGetEditDC(ped,TRUE); xposition = LOWORD(MLIchToXYPos(ped, hdc, ped->ichCaret, prevLine)); ECReleaseEditDC(ped,hdc,TRUE);
/* Remember, MLIchToXYPos 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. */ if (xposition < 0) /* Scroll to the left */ MLScrollHandler(ped, WM_HSCROLL, EM_LINESCROLL, (xposition-(ped->rcFmt.right-ped->rcFmt.left)/3) /ped->aveCharWidth); else if (xposition > ped->rcFmt.right) /* Scroll to the right */ MLScrollHandler(ped, WM_HSCROLL, EM_LINESCROLL, (xposition-ped->rcFmt.right+ (ped->rcFmt.right-ped->rcFmt.left)/3)/ ped->aveCharWidth); } } xposition = (int)MLThumbPosFromPed(ped,TRUE); if (xposition != GetScrollPos(ped->hwnd, SB_VERT)) SetScrollPos(ped->hwnd, SB_VERT, xposition, TRUE);
xposition = (int)MLThumbPosFromPed(ped,FALSE); if (xposition != GetScrollPos(ped->hwnd, SB_HORZ)) SetScrollPos(ped->hwnd, SB_HORZ, xposition, TRUE);
return(fScrolled); }
void FAR PASCAL MLSetRectHandler(ped, lprect) register PED ped; LPRECT lprect; /*
* effects: Sets the edit control's format rect to be the rect specified if * reasonable. Rebuilds the lines if needed. */ { RECT rc;
CopyRect((LPRECT)&rc, lprect);
if (!(rc.right-rc.left) || !(rc.bottom-rc.top)) { if (ped->rcFmt.right - ped->rcFmt.left) { ped->fCaretHidden = 1; // then, hide it.
SetCaretPos(-20000, -20000); /* If rect is being set to zero width or height, and our formatting
rectangle is already defined, just return. */ return; }
SetRect((LPRECT)&rc, 0, 0, ped->aveCharWidth*10, ped->lineHeight); }
if (ped->fBorder) /* Shrink client area to make room for the border */ InflateRect((LPRECT)&rc, -(ped->cxSysCharWidth/2), -(ped->cySysCharHeight/4));
/*
* If resulting rectangle is too small to do anything with, don't change it */ if ((rc.right-rc.left < ped->aveCharWidth) || ((rc.bottom - rc.top)/ped->lineHeight == 0)) { // If the resulting rectangle is too small to display the caret, then
// do not display the caret.
ped->fCaretHidden = 1; SetCaretPos(-20000, -20000); /* If rect is too narrow or too short, do nothing */ return; } else ped->fCaretHidden = 0;
/* Calc number of lines we can display on the screen */ ped->ichLinesOnScreen = (rc.bottom - rc.top)/ped->lineHeight;
CopyRect((LPRECT)&ped->rcFmt, (LPRECT)&rc);
/* Get an integral number of lines on the screen */ ped->rcFmt.bottom = rc.top+ped->ichLinesOnScreen * ped->lineHeight;
/* Rebuild the chLines if we are word wrapping only */ if (ped->fWrap) { MLBuildchLines(ped, 0, 0, FALSE); // Update the ped->iCaretLine field properly based on ped->ichCaret
MLUpdateiCaretLine(ped); } }
/*******************/ /* MLEditWndProc() */ /*******************/ LONG FAR PASCAL MLEditWndProc(hwnd, ped, message, wParam, lParam) HWND hwnd; register PED ped; WORD message; register WORD wParam; LONG lParam; /* effects: Class procedure for all multi line edit controls.
Dispatches all messages to the appropriate handlers which are named as follows: SL (single line) prefixes all single line edit control procedures while EC (edit control) prefixes all common handlers.
The MLEditWndProc only handles messages specific to multi line edit controls. */
{ switch (message) { case WM_CHAR: /* wParam - the value of the key
lParam - modifiers, repeat count etc (not used) */ MLCharHandler(ped, wParam, 0); break;
case WM_CLEAR: /* wParam - not used
lParam - not used */ if (ped->ichMinSel != ped->ichMaxSel && !ped->fReadOnly) SendMessage(ped->hwnd, WM_CHAR, (WORD) BACKSPACE, 0L); break;
case WM_CUT: /* wParam - not used
lParam - not used */ if (ped->ichMinSel != ped->ichMaxSel && !ped->fReadOnly) MLKeyDownHandler(ped, VK_DELETE, SHFTDOWN); break;
case WM_ERASEBKGND: FillWindow(ped->hwndParent, hwnd, (HDC)wParam, CTLCOLOR_EDIT); return((LONG)TRUE);
case WM_GETDLGCODE: /* wParam - not used
lParam - not used */ /* Should also return DLGC_WANTALLKEYS for multiline edit controls */ ped->fInDialogBox=TRUE; /* Mark the ML edit ctrl as in a dialog box */ return(DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS | DLGC_WANTALLKEYS); break;
case WM_HSCROLL: case WM_VSCROLL: return(MLScrollHandler(ped, message, wParam, (int)lParam)); break;
case WM_KEYDOWN: /* wParam - virt keycode of the given key
lParam - modifiers such as repeat count etc. (not used) */ MLKeyDownHandler(ped, wParam, 0); break;
case WM_KILLFOCUS: /* wParam - handle of the window that receives the input focus
lParam - not used */ MLKillFocusHandler(ped); 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) MLMouseMotionHandler(ped, WM_MOUSEMOVE, ped->prevKeys,ped->ptPrevMouse); break;
case WM_MOUSEMOVE: if (!ped->fMouseDown) break; /* else 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 */ MLMouseMotionHandler(ped, message, wParam, MAKEPOINT(lParam)); 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(MLCreateHandler(hwnd, ped, (LPCREATESTRUCT) lParam)); break;
case WM_PAINT: /* wParam - officially not used (but some apps pass in a DC here)
lParam - not used */ MLPaintHandler(ped, wParam); break;
case WM_PASTE: /* wParam - not used
lParam - not used */ if (!ped->fReadOnly) MLPasteText(ped); break;
case WM_SETFOCUS: /* wParam - handle of window that loses the input focus (may be NULL)
lParam - not used */ MLSetFocusHandler(ped); break;
case WM_SETTEXT: /* wParam - not used
lParam - points to a null-terminated string that is used to set the window text. */ return(MLSetTextHandler(ped, (LPSTR)lParam)); 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 */ MLSizeHandler(ped); 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) MLInsertCrCrLf(ped); else MLStripCrCrLf(ped); MLBuildchLines(ped, 0, 0, FALSE); return((LONG)(ped->fFmtLines = wParam)); break;
case EM_GETHANDLE: /* wParam - not used
lParam - not used */ /* effects: 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 ECInsertText allocates an extra * byte for the NULL terminator. */ /**(LocalLock(ped->hText)+ped->cch) = 0;*/ /*LocalUnlock(ped->hText);*/ *(LMHtoP(ped->hText)+ped->cch) = 0; return((LONG)ped->hText); break;
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(MLGetLineHandler(ped, wParam, *(WORD FAR *)lParam, (LPSTR)lParam)); break;
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((LONG)MLIchToLineHandler(ped, wParam)); break;
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 returns the number of character positions that occur preceeding the first char in a given line. */ return((LONG)MLLineIndexHandler(ped, wParam)); break;
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 returned not including the length of any selected text. lParam - not used */ return((LONG)MLLineLengthHandler(ped, wParam)); break;
case EM_LINESCROLL: /* wParam - not used
lParam - Contains the number of lines and char positions to scroll */ MLScrollHandler(ped, WM_VSCROLL, EM_LINESCROLL, LOWORD(lParam)); MLScrollHandler(ped, WM_HSCROLL, EM_LINESCROLL, HIWORD(lParam)); break;
case EM_REPLACESEL: /* wParam - not used
lParam - Points to a null terminated replacement text. */ SwapHandle(&lParam); ECEmptyUndo(ped); MLDeleteText(ped); ECEmptyUndo(ped); SwapHandle(&lParam); MLInsertText(ped, (LPSTR)lParam, lstrlen((LPSTR)lParam), FALSE); ECEmptyUndo(ped); break;
case EM_SCROLL: /* Scroll the window vertically */ /* wParam - contains the command type
lParam - not used. */ return(MLScrollHandler(ped, WM_VSCROLL, wParam, (int)lParam)); break;
case EM_SETHANDLE: /* wParam - contains a handle to the text buffer
lParam - not used */ MLSetHandleHandler(ped, (HANDLE)wParam); break;
case EM_SETRECT: /* wParam - not used
lParam - Points to a RECT which specifies the new dimensions of the rectangle. */ MLSetRectHandler(ped, (LPRECT)lParam); /* Do a repaint of the whole client area since the app may have shrunk
* the rectangle for the text and we want to be able to erase the old * text. */ InvalidateRect(hwnd, (LPRECT)NULL, TRUE); UpdateWindow(hwnd); break;
case EM_SETRECTNP: /* wParam - not used
lParam - Points to a RECT which specifies the new dimensions of the rectangle. */ /* We don't do a repaint here. */ MLSetRectHandler(ped, (LPRECT)lParam); break;
case EM_SETSEL: /* wParam - not used
lParam - starting pos in lowword ending pos in high word */ MLSetSelectionHandler(ped, LOWORD(lParam), HIWORD(lParam)); break;
case WM_UNDO: case EM_UNDO: return(MLUndoHandler(ped)); break;
case EM_SETTABSTOPS: /* This sets the tab stop positions for multiline edit controls.
* wParam - Number of tab stops * lParam - Far ptr to a WORD array containing the Tab stop positions */ return(MLSetTabStops(ped, (int)wParam, (LPINT)lParam)); break;
default: return(DefWindowProc(hwnd,message,wParam,lParam)); break; }
return(1L); } /* MLEditWndProc */
void NEAR PASCAL MLDrawText(register PED ped, HDC hdc, ICH ichStart, ICH ichEnd) /* effects: draws the characters between ichstart and ichend.
*/ { DWORD textColorSave; DWORD bkColorSave; LONG xyPos; LONG xyNonLeftJustifiedStart; HBRUSH hBrush; ICH ich; PSTR pText; int line; ICH length; ICH length2; int xOffset; DWORD ext; RECT rc; BOOL fPartialLine = FALSE; BOOL fSelected = FALSE; BOOL fLeftJustified = TRUE; #ifdef WOW
HWND hwndSend; #endif
if (ped->fNoRedraw || !ped->ichLinesOnScreen) /* Just return if nothing to draw */ return;
/* Set initial state of dc */ #ifndef WOW
hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT); #else
if (!(hwndSend = GetParent(ped->hwnd))) hwndSend = ped->hwnd; SendMessage(hwndSend, WM_CTLCOLOR, (WORD)hdc, MAKELONG(ped->hwnd, CTLCOLOR_EDIT)); #endif
if ((WORD)ichStart < (WORD)ped->chLines[ped->screenStart]) { ichStart = ped->chLines[ped->screenStart]; if (ichStart > ichEnd) return; }
line = min(ped->screenStart+ped->ichLinesOnScreen,ped->cLines-1); ichEnd = umin(ichEnd, ped->chLines[line]+MLLineLength(ped, line));
line = MLIchToLineHandler(ped, ichStart); if (ped->format != ES_LEFT) { ichStart = ped->chLines[line]; fLeftJustified = FALSE; }
pText = LocalLock(ped->hText);
HideCaret(ped->hwnd); while (ichStart <= ichEnd) { StillWithSameLine: length2 = MLLineLength(ped, line); if (length2 < (ichStart - ped->chLines[line])) { goto NextLine; }
length = length2 - (ichStart - ped->chLines[line]);
xyPos = MLIchToXYPos(ped, hdc, ichStart, FALSE);
if (!fLeftJustified && ichStart == ped->chLines[line]) xyNonLeftJustifiedStart = xyPos;
/* Find out how many pixels we indent the line for funny formats */ if (ped->format != ES_LEFT) xOffset = MLCalcXOffset(ped, hdc, line); else xOffset = -ped->xOffset;
if (!(ped->ichMinSel == ped->ichMaxSel || ichStart >= ped->ichMaxSel || ichEnd < ped->ichMinSel || (!ped->fNoHideSel && !ped->fFocus))) { if (ichStart < ped->ichMinSel) { fSelected = FALSE; length2 = umin(ichStart+length, ped->ichMinSel)-ichStart; } else { fSelected = TRUE; length2 = umin(ichStart+length, ped->ichMaxSel)-ichStart; /* Select in the highlight colors */ bkColorSave = SetBkColor(hdc, ped->rgbHiliteBk); textColorSave = SetTextColor(hdc, ped->rgbHiliteText); } fPartialLine = (length != length2); length = length2; }
ext = ECTabTheTextOut(hdc, LOWORD(xyPos), HIWORD(xyPos), (LPSTR)(pText+ichStart), length, ped,/*iLeft+xOffset*/ped->rcFmt.left+xOffset, TRUE);
if (fSelected) { fSelected = FALSE; SetBkColor(hdc, bkColorSave); SetTextColor(hdc, textColorSave); }
if (fPartialLine) { fPartialLine = FALSE; ichStart += length; goto StillWithSameLine; }
/* Fill to end of line so use a very large width for this rectangle.
*/ SetRect((LPRECT)&rc, LOWORD(xyPos)+LOWORD(ext), HIWORD(xyPos), 32764, HIWORD(xyPos)+ped->lineHeight); ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, (LPRECT)&rc, "",0,0L); if (!fLeftJustified) { SetRect((LPRECT)&rc, ped->rcFmt.left, HIWORD(xyNonLeftJustifiedStart), LOWORD(xyNonLeftJustifiedStart), HIWORD(xyNonLeftJustifiedStart)+ped->lineHeight); ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE, (LPRECT)&rc, "",0,0L); }
NextLine: line++; if (ped->cLines > line) { ichStart = ped->chLines[line]; } else ichStart = ichEnd+1; }
LocalUnlock(ped->hText); ShowCaret(ped->hwnd); MLSetCaretPosition(ped,hdc); }
|