|
|
/*++
* * WOW v1.0 * * Copyright (c) 1991, Microsoft Corporation * * EDITSL.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. --*/
/****************************************************************************/ /* editsl.c - Edit controls rewrite. Version II of edit controls. */ /* */ /* */ /* Created: 24-Jul-88 davidds */ /****************************************************************************/
#define NO_LOCALOBJ_TAGS
#include "user.h"
#include "edit.h"
/****************************************************************************/ /* Single Line Support Routines */ /****************************************************************************/
void FAR PASCAL SLSetCaretPosition(ped,hdc) register PED ped; HDC hdc; /* effects: If the window has the focus, find where the caret belongs and move
* it there. */ { int xPosition; /* We will only position the caret if we have the focus since we don't want
* to move the caret while another window could own it. */ if (ped->fNoRedraw || !ped->fFocus) return;
xPosition = SLIchToLeftXPos(ped, hdc, ped->ichCaret); if (!ped->fAutoHScroll) /* Don't leet caret go out of bounds of edit contol if there is too much
* text in a non scrolling edit control. */ xPosition = min(xPosition, ped->rcFmt.right-1);
SetCaretPos(xPosition, ped->rcFmt.top); }
int NEAR PASCAL SLIchToLeftXPos(ped, hdc, ich) register PED ped; HDC hdc; ICH ich; /* effects: Given a character index, find its (left side) x coordinate within
* the ped->rcFmt rectangle assuming the character ped->screenStart is at * coordinates (ped->rcFmt.top, ped->rcFmt.left). A negative value is * returned if the character ich is to the left of ped->screenStart. WARNING: * ASSUMES AT MOST 1000 characters will be VISIBLE at one time on the screen. * There may be 64K total characters in the editcontrol, but we can only * display 1000 without scrolling. This shouldn't be a problem obviously. */ { int textExtent; register PSTR pText;
/* Check if we are adding lots and lots of chars. A paste for example could
* cause this and GetTextExtents could overflow on this. */ if (ich > ped->screenStart && ich - ped->screenStart > 1000) return(30000); if (ped->screenStart > ich && ped->screenStart - ich > 1000) return(-30000);
if (ped->fNonPropFont) return((ich-ped->screenStart)*ped->aveCharWidth + ped->rcFmt.left);
/* Check if password hidden chars are being used. */ if (ped->charPasswordChar) return((ich-ped->screenStart)*ped->cPasswordCharWidth+ped->rcFmt.left); pText = LocalLock(ped->hText);
if (ped->screenStart <= ich) { textExtent = LOWORD(GetTextExtent(hdc, (LPSTR)(pText + ped->screenStart), ich-ped->screenStart)); /* In case of signed/unsigned overflow since the text extent may be
* greater than maxint. This happens with long single line edit * controls. The rect we edit text in will never be greater than 30000 * pixels so we are ok if we just ignore them. */ if (textExtent < 0 || textExtent > 31000) textExtent = 30000; } else textExtent = (-1) * (int)LOWORD(GetTextExtent(hdc,(LPSTR)(pText + ich), ped->screenStart-ich));
LocalUnlock(ped->hText);
return(textExtent-ped->charOverhang + ped->rcFmt.left); }
/* effects: This finds out if the given ichPos falls within the current
* Selection range and if so returns TRUE; Else returns FALSE. */ BOOL NEAR PASCAL SLGetHiliteAttr(PED ped, ICH ichPos) { return((ichPos >= ped->ichMinSel) && (ichPos < ped->ichMaxSel)); }
/* effects: This takes care of erasing the old selection and drawing the new
* selection */ void NEAR PASCAL SLRepaintChangedSelection( PED ped, HDC hdc, ICH ichOldMinSel, ICH ichOldMaxSel) { BLOCK Blk[2]; 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])) { UpdateWindow(ped->hwnd); /* 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) SLDrawLine(ped, hdc, Blk[i].StPos, Blk[i].EndPos - Blk[i].StPos, SLGetHiliteAttr(ped, Blk[i].StPos)); } } }
void FAR PASCAL SLChangeSelection(ped, hdc, ichNewMinSel, ichNewMaxSel) 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; ICH ichOldMaxSel;
if (ichNewMinSel > ichNewMaxSel) { temp = ichNewMinSel; ichNewMinSel = ichNewMaxSel; ichNewMaxSel = temp; } ichNewMinSel = umin(ichNewMinSel, ped->cch); ichNewMaxSel = umin(ichNewMaxSel, ped->cch);
/* Preserve the Old selection */ ichOldMinSel = ped->ichMinSel; ichOldMaxSel = ped->ichMaxSel;
/* Set new selection */ ped->ichMinSel = ichNewMinSel; ped->ichMaxSel = ichNewMaxSel;
/* We will find the intersection of current selection rectangle with the new
* selection rectangle. We will then invert the parts of the two rectangles * not in the intersection. */
if (!ped->fNoRedraw && (ped->fFocus || ped->fNoHideSel)) { if (ped->fFocus) HideCaret(ped->hwnd); SLRepaintChangedSelection(ped, hdc, ichOldMinSel, ichOldMaxSel); SLSetCaretPosition(ped,hdc); if (ped->fFocus) ShowCaret(ped->hwnd); } }
void NEAR PASCAL SLDrawLine(ped, hdc, ichStart, iCount, fSelStatus)
register PED ped; register HDC hdc; ICH ichStart; int iCount; BOOL fSelStatus;
/* This draws the line starting from ichStart, iCount number of characters;
* fSelStatus is TRUE, if it is to be drawn with the "selection" attribute. */ { RECT rc; HBRUSH hBrush; PSTR pText; DWORD rgbSaveBk; DWORD rgbSaveText; int iStCount; DWORD rgbGray=0;
if (ped->fNoRedraw) return;
if (ichStart < ped->screenStart) { if (ichStart+iCount < ped->screenStart) return;
iCount = iCount - (ped->screenStart-ichStart); ichStart = ped->screenStart; }
CopyRect((LPRECT)&rc,(LPRECT)&ped->rcFmt);
/* Set the proper clipping rectangle */ ECSetEditClip(ped, hdc);
pText = (PSTR) LocalLock(ped->hText);
/* Calculates the rectangle area to be wiped out */ if (iStCount = ichStart - ped->screenStart) { if (ped->charPasswordChar) rc.left += ped->cPasswordCharWidth * iStCount; else rc.left += LOWORD(GetTextExtent(hdc, (LPSTR)(pText + ped->screenStart), iStCount)) - ped->charOverhang; }
if (ped->charPasswordChar) rc.right = rc.left + ped->cPasswordCharWidth * iCount; else rc.right = rc.left + LOWORD(GetTextExtent(hdc, (LPSTR)(pText + ichStart), iCount)); /* Set the background mode before calling GetControlBrush so that the app
* can change it to TRANSPARENT if it wants to. */ SetBkMode(hdc, OPAQUE);
if (fSelStatus) { hBrush = ped->hbrHiliteBk; rgbSaveBk = SetBkColor(hdc, ped->rgbHiliteBk); rgbSaveText = SetTextColor(hdc, ped->rgbHiliteText); } else { /* We always want to send this so that the app has a chance to muck with
* the DC */ hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT); }
if (ped->fDisabled && (rgbGray = GetSysColor(COLOR_GRAYTEXT))) { /* Grey the text in the edit control if disabled.
*/ rgbSaveText = SetTextColor(hdc, rgbGray); }
/* Erase the rectangular area before text is drawn. Note that we inflate the
* rect by 1 so that the selection color has a one pixel border around the * text. */ InflateRect((LPRECT)&rc, 0, 1); /* Use paint rect so that the brush gets aligned if dithered.
*/ PaintRect(ped->hwndParent, ped->hwnd, hdc, hBrush, (LPRECT)&rc); InflateRect((LPRECT)&rc, 0, -1); if (ped->charPasswordChar) { for (iStCount = 0; iStCount < iCount; iStCount++) TextOut(hdc, rc.left+iStCount*ped->cPasswordCharWidth, rc.top, (LPSTR)&ped->charPasswordChar, 1); } else TextOut(hdc,rc.left,rc.top,(LPSTR)(pText+ichStart),iCount);
if (fSelStatus || rgbGray) { SetTextColor(hdc, rgbSaveText); if (fSelStatus) SetBkColor(hdc, rgbSaveBk);
}
LocalUnlock(ped->hText); }
int NEAR PASCAL SLGetBlkEnd(ped, ichStart, ichEnd, lpfStatus) register PED ped; register ICH ichStart; ICH ichEnd; BOOL FAR *lpfStatus; /* Given a Starting point and and end point, this function returns whether the
* first few characters fall inside or outside the selection block and if so, * howmany characters? */ { *lpfStatus = FALSE; if (ichStart >= ped->ichMinSel) { if(ichStart >= ped->ichMaxSel) return(ichEnd - ichStart); *lpfStatus = TRUE; return(min(ichEnd, ped->ichMaxSel) - ichStart); } return(min(ichEnd, ped->ichMinSel) - ichStart); }
void NEAR PASCAL SLDrawText(ped, hdc, ichStart) register PED ped; register HDC hdc; ICH ichStart; /* effects: Draws text for a single line edit control in the rectangle
* specified by ped->rcFmt. If ichStart == 0, starts drawing text at the left * side of the window starting at character index ped->screenStart and draws * as much as will fit. If ichStart > 0, then it appends the characters * starting at ichStart to the end of the text showing in the window. (ie. We * are just growing the text length and keeping the left side * (ped->screenStart to ichStart characters) the same. Assumes the hdc came * from ECGetEditDC so that the caret and such are properly hidden. */ { ICH cchToDraw; RECT rc;
PSTR pText; BOOL fSelStatus; int iCount; ICH ichEnd; BOOL fNoSelection;
if (ped->fNoRedraw) return;
if (ichStart == 0) ichStart = ped->screenStart;
CopyRect((LPRECT)&rc,(LPRECT)&ped->rcFmt);
/* Find out how many characters will fit on the screen so that we don't do
* any needless drawing. */
pText = (PSTR) LocalLock(ped->hText);
cchToDraw = ECCchInWidth(ped, hdc, (LPSTR)(pText+ped->screenStart), ped->cch-ped->screenStart, rc.right - rc.left); ichEnd = ped->screenStart + cchToDraw;
/*
* There is no selection if, * 1. MinSel and MaxSel are equal OR * 2. (This has lost the focus AND Selection is to be hidden) */ fNoSelection = ((ped->ichMinSel == ped->ichMaxSel) || (!ped->fFocus && !ped->fNoHideSel)); while (ichStart < ichEnd) { if (fNoSelection) { fSelStatus = FALSE; iCount = ichEnd - ichStart; } else iCount = SLGetBlkEnd(ped, ichStart, ichEnd, (BOOL FAR *)&fSelStatus); SLDrawLine(ped, hdc, ichStart, iCount, fSelStatus); ichStart += iCount; }
if (cchToDraw) { /* Check if password hidden chars are being used. */ if (ped->charPasswordChar) rc.left += ped->cPasswordCharWidth * cchToDraw; else rc.left += LOWORD(GetTextExtent(hdc, (LPSTR)(pText + ped->screenStart), cchToDraw)); }
LocalUnlock(ped->hText);
/* Check if anything to be erased on the right hand side */ if (rc.left < rc.right) { SetBkMode(hdc, OPAQUE); /* Erase the rectangular area before text is drawn. Note that we inflate
* the rect by 1 so that the selection color has a one pixel border * around the text. */ InflateRect((LPRECT)&rc, 0, 1); PaintRect(ped->hwndParent, ped->hwnd, hdc, NULL, (LPRECT)&rc); } SLSetCaretPosition(ped, hdc); }
BOOL FAR PASCAL SLScrollText(ped, hdc) register PED ped; HDC hdc; /* effects: Scrolls the text to bring the caret into view. If the text is
* scrolled, the current selection is unhighlighted. Returns TRUE if the text * is scrolled else returns false. */ { register PSTR pText; ICH scrollAmount = (ped->rcFmt.right-ped->rcFmt.left)/4/ped->aveCharWidth+1; ICH newScreenStartX = ped->screenStart;
if (!ped->fAutoHScroll) return(FALSE);
/* Calculate the new starting screen position */ if (ped->ichCaret <= ped->screenStart) { /* Caret is to the left of the starting text on the screen we must
* scroll the text backwards to bring it into view. Watch out when * subtracting unsigned numbers when we have the possibility of going * negative. */ if (ped->ichCaret > scrollAmount) newScreenStartX = ped->ichCaret - scrollAmount; else newScreenStartX = 0; } else { pText = (PSTR) LocalLock(ped->hText); if ((ped->ichCaret != ped->screenStart) && (ECCchInWidth(ped, hdc, (LPSTR)(pText+ped->screenStart), ped->ichCaret - ped->screenStart, ped->rcFmt.right-ped->rcFmt.left) < ped->ichCaret - ped->screenStart)) { newScreenStartX = ((ped->ichCaret < scrollAmount*2) ? 0 : ped->ichCaret - scrollAmount*2); } LocalUnlock(ped->hText); }
#ifdef FE_SB
pText = (PSTR) LocalLock(ped->hText); newScreenStartX = ECAdjustIch( ped,pText,newScreenStartX ); LocalUnlock(ped->hText); #endif
if (ped->screenStart != newScreenStartX) { ped->screenStart = newScreenStartX; SLDrawText(ped, hdc, 0); /* Caret pos is set by drawtext */ return(TRUE); }
return(FALSE);
}
ICH FAR PASCAL SLInsertText(ped, lpText, cchInsert) register PED ped; LPSTR lpText; register ICH cchInsert; /* 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 and send a EN_MAXTEXT * notification code to the parent of the ec. Also, if !fAutoHScroll, then we * only allow as many chars as will fit in the client rectangle. The number of * characters actually added is returned (could be 0). If we can't allocate * the required space, we notify the parent with EN_ERRSPACE and no characters * are added. */ { HDC hdc; PSTR pText; ICH cchInsertCopy = cchInsert; int textWidth;
/* First determine exactly how many characters from lpText we can insert
* into the ped. */ if (!ped->fAutoHScroll) { pText = (PSTR)LocalLock(ped->hText); hdc = ECGetEditDC(ped, TRUE);
/* If ped->fAutoHScroll bit is not set, then we only insert as many
* characters as will fit in the ped->rcFmt rectangle upto a maximum of * ped->cchTextMax - ped->cch characters. Note that if password style is * on, we allow the user to enter as many chars as the number of * password chars which fit in the rect. */ if (ped->cchTextMax <= ped->cch) cchInsert = 0; else { cchInsert = umin(cchInsert, (unsigned)(ped->cchTextMax - ped->cch)); if (ped->charPasswordChar) textWidth = ped->cch * ped->cPasswordCharWidth; else textWidth = LOWORD(GetTextExtent(hdc, (LPSTR)pText, ped->cch));
cchInsert = umin(cchInsert, ECCchInWidth(ped, hdc, lpText, cchInsert, ped->rcFmt.right-ped->rcFmt.left- textWidth)); }
LocalUnlock(ped->hText); ECReleaseEditDC(ped, hdc, TRUE); } else { if (ped->cchTextMax <= ped->cch) cchInsert = 0; else cchInsert = umin((unsigned)(ped->cchTextMax - ped->cch), cchInsert); }
/* Now try actually adding the text to the ped */ if (cchInsert && !ECInsertText(ped, lpText, cchInsert)) { ECNotifyParent(ped, EN_ERRSPACE); return(0); }
if (cchInsert) ped->fDirty = TRUE; /* Set modify flag */
if (cchInsert < cchInsertCopy) /* Notify parent that we couldn't insert all the text requested */ ECNotifyParent(ped, EN_MAXTEXT);
/* Update selection extents and the caret position. Note that ECInsertText
* updates ped->ichCaret, ped->ichMinSel, and ped->ichMaxSel to all be after * the inserted text. */
return(cchInsert); }
ICH PASCAL NEAR SLPasteText(register PED ped) /* effects: Pastes a line of text from the clipboard into the edit control
* starting at ped->ichMaxSel. Updates ichMaxSel and ichMinSel to point to * the end of the inserted text. Notifies the parent if space cannot be * allocated. Returns how many characters were inserted. */ { HANDLE hData; LPSTR lpchClip; LPSTR lpchClip2; register ICH cchAdded; ICH clipLength;
if (!OpenClipboard(ped->hwnd)) return(0);
if (!(hData = GetClipboardData(CF_TEXT))) { CloseClipboard(); return(0); } lpchClip2 = lpchClip = (LPSTR) GlobalLock(hData);
/* Find the first carrage return or line feed. Just add text to that point.
*/ clipLength = (WORD)lstrlen(lpchClip); for (cchAdded = 0; cchAdded < clipLength; cchAdded++) if (*lpchClip2++ == 0x0D) break;
/* Insert the text (SLInsertText checks line length) */ cchAdded = SLInsertText(ped, lpchClip, cchAdded);
GlobalUnlock(hData); CloseClipboard();
return(cchAdded); }
void NEAR PASCAL SLReplaceSelHandler(register PED ped, LPSTR lpText) /* effects: Replaces the text in the current selection with the given text.
*/ { BOOL fUpdate; HDC hdc;
SwapHandle(&lpText); ECEmptyUndo(ped); fUpdate = (BOOL)ECDeleteText(ped); ECEmptyUndo(ped); SwapHandle(&lpText); fUpdate = (BOOL)SLInsertText(ped, lpText, lstrlen(lpText)) || fUpdate; ECEmptyUndo(ped); if (fUpdate) { ECNotifyParent(ped, EN_UPDATE); hdc = ECGetEditDC(ped,FALSE); if (!SLScrollText(ped,hdc)) SLDrawText(ped,hdc,0); ECReleaseEditDC(ped,hdc,FALSE); ECNotifyParent(ped,EN_CHANGE); } }
void FAR PASCAL SLCharHandler(ped, keyValue, keyMods) register PED ped; WORD keyValue; int keyMods; /* effects: Handles character input (really, no foolin')
*/ { register HDC hdc; unsigned char keyPress = LOBYTE(keyValue); BOOL updateText = FALSE; #ifdef FE_SB
PSTR pText; int InsertTextLen = 0; WORD DBCSkey; #endif
if (ped->fMouseDown || ped->fReadOnly) /* Don't do anything if we are in the middle of a mousedown deal or if
* this is a read only edit control. */ return;
if ((keyPress == BACKSPACE) || (keyPress >= ' ')) { /* Delete the selected text if any */ if (ECDeleteText(ped)) updateText=TRUE; }
switch(keyPress) { case BACKSPACE: /* 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 */ #ifdef FE_SB
pText = LMHtoP( ped->hText ); if( ECAnsiPrev( ped,pText, pText+ped->ichMinSel) == pText+ped->ichMinSel-2) ped->ichMinSel--; #endif
ped->ichMinSel--; (void) ECDeleteText(ped); updateText = TRUE; } break;
default: if (keyPress >= ' ') { #ifdef FE_SB
InsertTextLen = 1; if( IsDBCSLeadByte( keyPress ) ) if( ( DBCSkey = DBCSCombine( ped->hwnd, keyPress ) ) != NULL ) if( SLInsertText( ped, (LPSTR)&DBCSkey, 2 ) == 2){ InsertTextLen = 2; updateText = TRUE; }else MessageBeep(0); else MessageBeep(0); else #endif
if (SLInsertText(ped, (LPSTR) &keyPress, 1)) updateText = TRUE; else /* Beep. Since we couldn't add the text */ MessageBeep(0); } else /* User hit an illegal control key */ MessageBeep(0);
break; }
if (updateText) { /* Dirty flag (ped->fDirty) was set when we inserted text */ ECNotifyParent(ped, EN_UPDATE); hdc = ECGetEditDC(ped,FALSE); if (!SLScrollText(ped,hdc)) #ifdef FE_SB
SLDrawText(ped,hdc,(ped->ichCaret == 0 ? 0 :ped->ichCaret - InsertTextLen)); #else
SLDrawText(ped,hdc,(ped->ichCaret == 0 ? 0 :ped->ichCaret - 1)); #endif
ECReleaseEditDC(ped,hdc,FALSE); ECNotifyParent(ped,EN_CHANGE); }
}
void NEAR PASCAL SLKeyDownHandler(register PED ped, WORD virtKeyCode, int keyMods) /* effects: Handles cursor movement and other VIRT KEY stuff. keyMods allows
* us to make SLKeyDownHandler calls and specify if the modifier keys (shift * and control) are up or down. This is useful for imnplementing the * cut/paste/clear messages for single line edit controls. If keyMods == 0, * we get the keyboard state using GetKeyState(VK_SHIFT) etc. Otherwise, the * bits in keyMods define the state of the shift and control keys. */ { HDC hdc;
/* Variables we will use for redrawing the updated text */ register ICH newMaxSel = ped->ichMaxSel; ICH newMinSel = ped->ichMinSel;
/* Flags for drawing the updated text */ BOOL updateText = FALSE; BOOL changeSelection = FALSE; /* new selection is specified by
newMinSel, newMaxSel */
/* Comparisons we do often */ BOOL MinEqMax = (newMaxSel == newMinSel); BOOL MinEqCar = (ped->ichCaret == newMinSel); BOOL MaxEqCar = (ped->ichCaret == newMaxSel);
/* State of shift and control keys. */ int scState = 0;
/* Combo box support */ BOOL fIsListVisible; BOOL fIsExtendedUI;
#ifdef FE_SB
PSTR pText; #endif
if (ped->fMouseDown) { /* If we are in the middle of a mouse down handler, then don't do
* anything. ie. ignore keyboard input. */ return; }
if (!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_UP: if (ped->listboxHwnd) { /* Handle Combobox support */ fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L); fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L);
if (!fIsListVisible && fIsExtendedUI) { /* For TandyT
*/ DropExtendedUIListBox: /* Since an extendedui combo box doesn't do anything on f4, we
* turn off the extended ui, send the f4 to drop, and turn it * back on again. */ SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 0, 0L); SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0L); SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 1, 0L); return; } else goto SendKeyToListBox; }
/* else fall through */ 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 */ #ifdef FE_SB
pText = LMHtoP( ped->hText ); if( ECAnsiPrev( ped, pText, pText + ped->ichCaret ) == pText + ped->ichCaret - 2 ) ped->ichCaret--; #endif
ped->ichCaret--; 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 */ #ifdef FE_SB
pText = LMHtoP( ped->hText ); if( ECAnsiPrev( ped, pText, pText + ped->ichCaret ) == pText + ped->ichCaret - 2 ) ped->ichCaret--; #endif
ped->ichCaret--; 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_DOWN: if (ped->listboxHwnd) { /* Handle Combobox support */ fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L); fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L);
if (!fIsListVisible && fIsExtendedUI) { /* For TandyT
*/ goto DropExtendedUIListBox; } else goto SendKeyToListBox; }
/* else fall through */ 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 */ #ifdef FE_SB
pText = LMHtoP( ped->hText ) + ped->ichCaret; if( ECIsDBCSLeadByte( ped, *pText ) ) ped->ichCaret++; #endif
ped->ichCaret++; 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 */ #ifdef FE_SB
pText = LMHtoP( ped->hText ) + ped->ichCaret; if( ECIsDBCSLeadByte( ped, *pText ) ) ped->ichCaret++; #endif
ped->ichCaret++; 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_HOME: ped->ichCaret = 0; switch (scState) { case NONEDOWN: case CTRLDOWN: /* Clear selection, move caret home */ newMaxSel = newMinSel = ped->ichCaret; break;
case SHFTDOWN: case SHCTDOWN: /* Extend selection, move caret home */ if (MaxEqCar && !MinEqMax) { /* Reduce/negate selection extent */ newMinSel = 0; newMaxSel = ped->ichMinSel; } else /* Extend selection extent */ newMinSel = ped->ichCaret; break; }
changeSelection = TRUE; break;
case VK_END: newMaxSel = ped->ichCaret = ped->cch; switch (scState) { case NONEDOWN: case CTRLDOWN: /* Clear selection, move caret to end of text */ newMinSel = ped->cch; break;
case SHFTDOWN: case SHCTDOWN: /* 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_DELETE: if (ped->fReadOnly) break;
switch (scState) { case NONEDOWN: /* Clear selection. If no selection, delete (clear) character
* right. */ if ((ped->ichMaxSel<ped->cch) && (ped->ichMinSel==ped->ichMaxSel)) { /* Move cursor forwards and simulate a backspace.
*/ #ifdef FE_SB
pText = LMHtoP( ped->hText ) + ped->ichCaret; if( ECIsDBCSLeadByte( ped, *pText ) ) ped->ichCaret++; #endif
ped->ichCaret++; ped->ichMaxSel = ped->ichMinSel = ped->ichCaret; SLCharHandler(ped, (WORD) BACKSPACE, 0); } if (ped->ichMinSel != ped->ichMaxSel) SLCharHandler(ped, (WORD) BACKSPACE, 0); 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 simulating 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. */ SLCharHandler(ped, (WORD) BACKSPACE, 0); break;
case CTRLDOWN: /* Delete to end of line if no selection else delete (clear)
* selection. */ if ((ped->ichMaxSel<ped->cch) && (ped->ichMinSel==ped->ichMaxSel)) { /* Move cursor to end of line and simulate a backspace.
*/ ped->ichMaxSel = ped->ichCaret = ped->cch; } if (ped->ichMinSel != ped->ichMaxSel) SLCharHandler(ped, (WORD) BACKSPACE, 0); break; } /* No need to update text or selection since BACKSPACE message does it
* for us. */ break;
case VK_INSERT: switch (scState) { case CTRLDOWN: /* Copy current selection to clipboard */ SendMessage(ped->hwnd, WM_COPY, (WORD)NULL, (LONG)NULL); break;
case SHFTDOWN: if (ped->fReadOnly) break;
/* Insert contents of clipboard (PASTE) */ /* Unhighlight current selection and delete it, if any */ ECDeleteText(ped); SLPasteText(ped); updateText = TRUE; ECNotifyParent(ped, EN_UPDATE); break; } break;
case VK_F4: case VK_PRIOR: case VK_NEXT: /* Send keys to the listbox if we are a part of a combo box. This
* assumes the listbox ignores keyup messages which is correct right * now. */ if (ped->listboxHwnd) { SendKeyToListBox: /* Handle Combobox support */ SendMessage(ped->listboxHwnd, WM_KEYDOWN, virtKeyCode, 0L); return; } }
if (changeSelection || updateText) { hdc = ECGetEditDC(ped,FALSE); /* Scroll if needed */ SLScrollText(ped,hdc);
if (changeSelection) SLChangeSelection(ped,hdc,newMinSel,newMaxSel); if (updateText) SLDrawText(ped,hdc,0);
/* SLSetCaretPosition(ped,hdc);*/ ECReleaseEditDC(ped,hdc,FALSE); if (updateText) ECNotifyParent(ped, EN_CHANGE); }
}
ICH NEAR PASCAL SLMouseToIch(ped, hdc, mousePt) register PED ped; HDC hdc; POINT mousePt; /* effects: Returns the closest cch to where the mouse point is.
*/ { register PSTR pText; int width = mousePt.x; int textWidth; ICH cch;
if (width <= ped->rcFmt.left) { /* Return either the first non visible character or return 0 if at
* beginning of text */ if (ped->screenStart) return(ped->screenStart - 1); else return(0); }
if (width > ped->rcFmt.right) { pText = LocalLock(ped->hText);
/* Return last char in text or one plus the last char visible */ cch = ECCchInWidth(ped, hdc, (LPSTR)(pText+ped->screenStart), ped->cch - ped->screenStart, ped->rcFmt.right-ped->rcFmt.left) + ped->screenStart; LocalUnlock(ped->hText); if (cch >= ped->cch) return(ped->cch); else return(cch+1); }
/* Check if password hidden chars are being used. */ if (ped->charPasswordChar) return(umin((width-ped->rcFmt.left)/ped->cPasswordCharWidth,ped->cch));
if (!ped->cch) return(0);
pText = LocalLock(ped->hText); cch = ped->cch;
while(((textWidth = LOWORD(GetTextExtent(hdc, (LPSTR)(pText+ped->screenStart), cch-ped->screenStart))) > (width-ped->rcFmt.left)) && (cch-ped->screenStart) ) { /* For that "feel" */ if ((textWidth - ped->aveCharWidth/2) < (width-ped->rcFmt.left)) break;
cch--; }
#ifdef FE_SB
cch = ECAdjustIch( ped, pText, cch ); #endif
LocalUnlock(ped->hText); return(cch); }
void NEAR PASCAL SLMouseMotionHandler(ped, message, virtKeyDown, mousePt) register PED ped; WORD message; WORD virtKeyDown; POINT mousePt; { LONG selection; BOOL changeSelection = FALSE;
HDC hdc = ECGetEditDC(ped,FALSE); ICH newMaxSel = ped->ichMaxSel; ICH newMinSel = ped->ichMinSel;
ICH mouseIch = SLMouseToIch(ped, hdc, mousePt);
#ifdef FE_SB
LPSTR pText;
pText = LocalLock( ped->hText ); mouseIch = ECAdjustIch( ped, pText, mouseIch ); LocalUnlock( ped->hText ); #endif
switch (message) { case WM_LBUTTONDBLCLK: /* Note that we don't have to worry about this control having the focus
* since it got it when the WM_LBUTTONDOWN message was first sent. If * shift key is down, extend selection to word we double clicked on else * clear current selection and select word. */ #ifdef FE_SB
pText = LocalLock( ped->hText ) + ped->ichCaret; selection = ECWord(ped,ped->ichCaret, (ECIsDBCSLeadByte(ped,*pText) && ped->ichCaret < ped->cch) ? FALSE : TRUE ); LocalUnlock( ped->hText ); #else
selection = ECWord(ped,ped->ichCaret,TRUE); #endif
newMinSel = LOWORD(selection); newMaxSel = ped->ichCaret = HIWORD(selection); changeSelection = TRUE; /* Set mouse down to false so that the caret isn't reposition on the
* mouseup message or on an accidental move... */ ped->fMouseDown = FALSE; break;
case WM_MOUSEMOVE: if (ped->fMouseDown) { changeSelection = TRUE; /* Extend selection, move caret word right */ if ((ped->ichMinSel == ped->ichCaret) && (ped->ichMinSel != ped->ichMaxSel)) { /* Reduce selection extent */ newMinSel = ped->ichCaret = mouseIch; newMaxSel = ped->ichMaxSel; } else /* Extend selection extent */ newMaxSel = ped->ichCaret=mouseIch; } break;
case WM_LBUTTONDOWN: /*
* If we currently don't have the focus yet, try to get it. */ if (!ped->fFocus) { if (!ped->fNoHideSel) /* Clear the selection before setting the focus so that we don't
* get refresh problems and flicker. Doesn't matter since the * mouse down will end up changing it anyway. */ ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;
SetFocus(ped->hwnd); /* If we are part of a combo box, then this is the first time the
* edit control is getting the focus so we just want to highlight * the selection and we don't really want to position the caret. */ if (ped->listboxHwnd) break; }
if (ped->fFocus) { /* Only do this if we have the focus since a clever app may not want
* to give us the focus at the SetFocus call above. */ 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 = mouseIch; } else { /* Shiftkey is down so we want to maintain the current selection
* (if any) and just extend or reduce it */ if (ped->ichMinSel == ped->ichCaret) newMinSel = ped->ichCaret = mouseIch; else newMaxSel = ped->ichCaret = mouseIch; } } break;
case WM_LBUTTONUP: if (ped->fMouseDown) { ReleaseCapture(); /*SLSetCaretPosition(ped,hdc);*/ ped->fMouseDown = FALSE; } break; }
if (changeSelection) { SLScrollText(ped,hdc); SLChangeSelection(ped, hdc, newMinSel, newMaxSel); }
ECReleaseEditDC(ped,hdc,FALSE);
}
void NEAR PASCAL SLPaintHandler(ped,althdc) register PED ped; HDC althdc; /* effects: Handles painting of the edit control window. Draws the border if
* necessary and draws the text in its current state. */ { HWND hwnd = ped->hwnd; HBRUSH hBrush; register HDC hdc; PAINTSTRUCT paintstruct; RECT rcEdit; HANDLE hOldFont;
/* Had to put in hide/show carets. The first one needs to be done before
* beginpaint to correctly paint the caret if part is in the update region * and part is out. The second is for 1.03 compatibility. It breaks * micrografix's worksheet edit control if not there. */
HideCaret(hwnd);
/* Allow subclassing hdc */ if (!althdc) hdc = BeginPaint(hwnd, (PAINTSTRUCT FAR *)&paintstruct); else hdc = althdc;
HideCaret(hwnd);
if (!ped->fNoRedraw && IsWindowVisible(ped->hwnd)) { /* Erase the background since we don't do it in the erasebkgnd message.
*/ hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT); FillWindow(ped->hwndParent, hwnd, hdc, hBrush);
if (ped->fBorder) { GetClientRect(hwnd, (LPRECT)&rcEdit); DrawFrame(hdc, (LPRECT)&rcEdit, 1, DF_WINDOWFRAME); }
if (ped->hFont) /* We have to select in the font since this may be a subclassed dc
* or a begin paint dc which hasn't been initialized with out fonts * like ECGetEditDC does. */ hOldFont = SelectObject(hdc, ped->hFont);
SLDrawText(ped, hdc, 0);
if (ped->hFont && hOldFont) SelectObject(hdc, hOldFont);
} ShowCaret(hwnd);
if (!althdc) EndPaint(hwnd, (LPPAINTSTRUCT)&paintstruct);
ShowCaret(hwnd); }
void NEAR PASCAL SLSetFocusHandler(ped) register PED ped; /* effects: Gives the edit control the focus and notifies the parent
* EN_SETFOCUS. */ { register HDC hdc;
if (!ped->fFocus) { UpdateWindow(ped->hwnd);
ped->fFocus = TRUE; /* Set focus */
/* We don't want to muck with the caret since it isn't created. */ hdc = ECGetEditDC(ped,TRUE);
/* Show the current selection. Only if the selection was hidden when we
* lost the focus, must we invert (show) it. */ if (!ped->fNoHideSel) SLDrawText(ped, hdc, 0);
/* Create the caret. Add in the +1 because we have an extra pixel for
* highlighting around the text. If the font is at least as wide as the * system font, use a wide caret else use a 1 pixel wide caret. */ CreateCaret(ped->hwnd, (HBITMAP)NULL, (ped->cxSysCharWidth > ped->aveCharWidth ? 1 : 2), ped->lineHeight+1); SLSetCaretPosition(ped,hdc); ECReleaseEditDC(ped,hdc,TRUE); ShowCaret(ped->hwnd); }
/* Notify parent we have the focus */ ECNotifyParent(ped, EN_SETFOCUS); }
void NEAR PASCAL SLKillFocusHandler(ped, newFocusHwnd) register PED ped; HWND newFocusHwnd; /* effects: The edit control loses the focus and notifies the parent via
* EN_KILLFOCUS. */ { RECT rcEdit;
if (ped->fFocus) { /* Destroy the caret */ HideCaret(ped->hwnd); DestroyCaret();
ped->fFocus = FALSE; /* Clear focus */
/* Do this only if we still have the focus. But we always notify the
* parent that we lost the focus whether or not we originally had the * focus. */ /* Hide the current selection if needed */ if (!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel)) { GetClientRect(ped->hwnd, (LPRECT)&rcEdit); if (ped->fBorder && rcEdit.right-rcEdit.left && rcEdit.bottom-rcEdit.top) { /* Don't invalidate the border so that we avoid flicker */ InflateRect((LPRECT)&rcEdit, -1, -1); } InvalidateRect(ped->hwnd, (LPRECT)&rcEdit, FALSE); UpdateWindow(ped->hwnd);
#if 0
SLSetSelectionHandler(ped, ped->ichCaret, ped->ichCaret); #endif
} }
/* If we aren't a combo box, notify parent that we lost the focus.
*/ if (!ped->listboxHwnd) ECNotifyParent(ped, EN_KILLFOCUS); else { /* This editcontrol is part of a combo box and is losing the focus. If
* the focus is NOT being sent to another control in the combo box * window, then it means the combo box is losing the focus. So we will * notify the combo box of this fact. */ if (!IsChild(ped->hwndParent, newFocusHwnd)) { /* Focus is being sent to a window which is not a child of the combo
* box window which implies that the combo box is losing the focus. * Send a message to the combo box informing him of this fact so * that he can clean up... */ SendMessage(ped->hwndParent, CBEC_KILLCOMBOFOCUS, 0, 0L); } } }
/*******************/ /* SLEditWndProc() */ /*******************/ LONG FAR PASCAL SLEditWndProc(hwnd, ped, message, wParam, lParam) HWND hwnd; register PED ped; WORD message; register WORD wParam; LONG lParam; /* effects: Class procedure for all single line edit controls.
Dispatches all messages to the appropriate handlers which are named as follows: SL (single line) prefixes all single line edit control procedures while EC (edit control) prefixes all common handlers.
The SLEditWndProc only handles messages specific to single line edit controls. */
{ /* Dispatch the various messages we can receive */ switch (message) { case WM_CLEAR: /* wParam - not used
lParam - not used */ /*
* Call SLKeyDownHandler with a VK_DELETE keycode to clear the selected * text. */ if (ped->ichMinSel != ped->ichMaxSel) SLKeyDownHandler(ped, VK_DELETE, NOMODIFY); break;
case WM_CHAR: /* wParam - the value of the key
lParam - modifiers, repeat count etc (not used) */ if (!ped->fEatNextChar) SLCharHandler(ped, wParam, 0); else ped->fEatNextChar = FALSE; break;
case WM_CUT: /* wParam - not used
lParam - not used */ /* Call SLKeyDownHandler with a VK_DELETE keycode to cut the selected
* text. (Delete key with shift modifier.) This is needed so that apps * can send us WM_PASTE messages. */ if (ped->ichMinSel != ped->ichMaxSel) SLKeyDownHandler(ped, VK_DELETE, SHFTDOWN); break;
case WM_ERASEBKGND: /* wParam - device context handle
lParam - not used */ /* We do nothing on this message and we don't want DefWndProc to do
* anything, so return 1 */ return(1L); break;
case WM_GETDLGCODE: /* wParam - not used
lParam - not used */ return(DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS); break;
case WM_KEYDOWN: /* wParam - virt keycode of the given key
lParam - modifiers such as repeat count etc. (not used) */ SLKeyDownHandler(ped, wParam, 0); break;
case WM_KILLFOCUS: /* wParam - handle of the window that receives the input focus
lParam - not used */ SLKillFocusHandler(ped, (HWND)wParam); break;
case WM_MOUSEMOVE: 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 */ SLMouseMotionHandler(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(SLCreateHandler(hwnd, ped, (LPCREATESTRUCT) lParam)); break;
case WM_PAINT: /* wParam - not used - actually sometimes used as a hdc when subclassing
lParam - not used */ SLPaintHandler(ped, wParam); break;
case WM_PASTE: /* wParam - not used
lParam - not used */ /* Call SLKeyDownHandler with a SHIFT VK_INSERT keycode to paste the
* clipboard into the edit control. This is needed so that apps can * send us WM_PASTE messages. */ SLKeyDownHandler(ped, VK_INSERT, SHFTDOWN); break;
case WM_SETFOCUS: /* wParam - handle of window that loses the input focus (may be NULL)
lParam - not used */ SLSetFocusHandler(ped); break;
case WM_SETTEXT: /* wParam - not used
lParam - points to a null-terminated string that is used to set the window text. */ return(SLSetTextHandler(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 */ SLSizeHandler(ped); return(0L); break;
case WM_SYSKEYDOWN: if (ped->listboxHwnd && /* Check if we are in a combo box */ (lParam & 0x20000000L)) /* Check if the alt key is down */ { /*
* Handle Combobox support. We want alt up or down arrow to behave * like F4 key which completes the combo box selection */ ped->fEatNextChar = FALSE; if (lParam & 0x1000000) { /* This is an extended key such as the arrow keys not on the
* numeric keypad so just drop the combobox. */ if (wParam == VK_DOWN || wParam == VK_UP) goto DropCombo; else goto foo; }
if (GetKeyState(VK_NUMLOCK) < 0) { ped->fEatNextChar = FALSE; /* If numlock down, just send all system keys to dwp */ goto foo; } else /* Num lock is up. Eat the characters generated by the key board
* driver. */ ped->fEatNextChar = TRUE;
if (!(wParam == VK_DOWN || wParam == VK_UP)) goto foo;
DropCombo: if (SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L) & 0x00000001) { /* Extended ui doesn't honor VK_F4. */ if (SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L)) return(SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 0, 0L)); else return(SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 1, 0L)); } else return(SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0L)); } foo: if (wParam == VK_BACK) { SendMessage(ped->hwnd, EM_UNDO, 0, 0L); break; } goto PassToDefaultWindowProc; break;
case EM_GETLINE: /* wParam - line number to copy (always the first line for SL)
lParam - buffer to copy text to. FIrst word is max # of bytes to copy */ return(ECGetTextHandler(ped, (*(WORD FAR *)lParam), (LPSTR)lParam)); break;
case EM_LINELENGTH: /* wParam - ignored
lParam - ignored */ return((LONG)ped->cch); break;
case EM_SETSEL: /* wParam - not used
lParam - starting pos in lowword ending pos in high word */ SLSetSelectionHandler(ped, LOWORD(lParam), HIWORD(lParam)); break;
case EM_REPLACESEL: /* wParam - not used
lParam - points to a null terminated string of replacement text */ SLReplaceSelHandler(ped, (LPSTR)lParam); break;
case WM_UNDO: case EM_UNDO: SLUndoHandler(ped); break; default: PassToDefaultWindowProc: return(DefWindowProc(hwnd,message,wParam,lParam)); break;
} /* switch (message) */
return(1L); } /* SLEditWndProc */
|