/*++ * * 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 (;iLinecLines;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]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->ichMaxSelcch) && (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->ichMaxSelcch) && (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); }