#include "ctlspriv.h" #pragma hdrstop #include "usrctl32.h" #include "edit.h" //---------------------------------------------------------------------------// // // Language pack notes: // With the language pack loaded all positional processing is based on // ped->xOffset rather than ped->ichScreenStart. The non-lpk optimisation of // maintaining ped->ichScreenStart doesn't work because of the // glyph reordering features of complex scripts. // //---------------------------------------------------------------------------// // // Forwards // VOID EditSL_ChangeSelection(PED, HDC, ICH, ICH); VOID EditSL_DrawLine(PED, HDC, int, int, ICH, int, BOOL); BOOL EditSL_Undo(PED); //---------------------------------------------------------------------------// // typedef BOOL (*FnGetTextExtentPoint)(HDC, PVOID, int, LPSIZE); //---------------------------------------------------------------------------// INT EditSL_CalcStringWidth(PED ped, HDC hdc, ICH ich, ICH cch) { if (cch == 0) { return 0; } if (ped->charPasswordChar) { return cch * ped->cPasswordCharWidth; } else { SIZE size; if (ped->fNonPropFont && !ped->fDBCS) { size.cx = cch * ped->aveCharWidth; } else { PSTR pText = Edit_Lock(ped); if (ped->fAnsi) { GetTextExtentPointA(hdc, (LPSTR)(pText + ich), cch, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)pText + ich, cch, &size); } Edit_Unlock(ped); } return size.cx - ped->charOverhang; } } //---------------------------------------------------------------------------// // // EditSL_CalcXOffsetLeft // // Calculates the starting offset for left-aligned strings. // INT EditSL_CalcXOffsetLeft(PED ped, HDC hdc, ICH ich) { int cch = (int)(ich - ped->ichScreenStart); if (cch <= 0) { return 0; } return EditSL_CalcStringWidth(ped, hdc, ped->ichScreenStart, cch); } //---------------------------------------------------------------------------// // // EditSL_CalcXOffsetSpecial // // Calculates the horizontal offset (indent) required for right or center // justified lines. // INT EditSL_CalcXOffsetSpecial(PED ped, HDC hdc, ICH ich) { PSTR pText; ICH cch, ichStart = ped->ichScreenStart; int cx; // // Calc the number of characters from start to right end. // pText = Edit_Lock(ped); cch = Edit_CchInWidth(ped, hdc, (LPSTR)(pText + ichStart * ped->cbChar), ped->cch - ichStart, ped->rcFmt.right - ped->rcFmt.left, TRUE); Edit_Unlock(ped); // // Once the last character of the string has been scrolled out of // the view, use normal offset calculation. // if (ped->ichScreenStart + cch < ped->cch) { return EditSL_CalcXOffsetLeft(ped, hdc, ich); } cx = ped->rcFmt.right - ped->rcFmt.left - EditSL_CalcStringWidth(ped, hdc, ichStart, cch); if (ped->format == ES_CENTER) { cx = max(0, cx / 2); } else if (ped->format == ES_RIGHT) { // // Subtract 1 so that the 1 pixel wide cursor will be in the visible // region on the very right side of the screen, mle does this. // cx = max(0, cx - 1); } return cx + EditSL_CalcStringWidth(ped, hdc, ichStart, ich - ichStart); } //---------------------------------------------------------------------------// // // EditSL_SetCaretPosition AorW // // If the window has the focus, find where the caret belongs and move // it there. // VOID EditSL_SetCaretPosition(PED ped, HDC hdc) { int xPosition; // // We will only position the caret if we have the focus since we don't want // to move the caret while another window could own it. // if (!ped->fFocus) { return; } if (ped->fCaretHidden) { SetCaretPos(-20000, -20000); return; } xPosition = EditSL_IchToLeftXPos(ped, hdc, ped->ichCaret); // // Don't let caret go out of bounds of edit control if there is too much // text. // if (ped->pLpkEditCallout) { xPosition += ped->iCaretOffset; xPosition = max(xPosition , 0); xPosition = min(xPosition, ped->rcFmt.right - 1 - ((ped->cxSysCharWidth > ped->aveCharWidth) ? 1 : 2)); } else { xPosition = min(xPosition, ped->rcFmt.right - ((ped->cxSysCharWidth > ped->aveCharWidth) ? 1 : 2)); } SetCaretPos(xPosition, ped->rcFmt.top); // // FE_IME EditSL_SetCaretPosition - ECImmSetCompostionWindow( CFS_POINT ) // if (g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0))) { Edit_ImmSetCompositionWindow(ped, xPosition, ped->rcFmt.top); } } //---------------------------------------------------------------------------// // // EditSL_IchToLeftXPos AorW // // Given a character index, find its (left side) x coordinate within // the ped->rcFmt rectangle assuming the character ped->ichScreenStart is at // coordinates (ped->rcFmt.top, ped->rcFmt.left). A negative value is // return ed if the character ich is to the left of ped->ichScreenStart. WARNING: // ASSUMES AT MOST 1000 characters will be VISIBLE at one time on the screen. // There may be 64K total characters in the editcontrol, but we can only // display 1000 without scrolling. This shouldn't be a problem obviously. // INT EditSL_IchToLeftXPos(PED ped, HDC hdc, ICH ich) { int textExtent; PSTR pText; SIZE size; int cchDiff; if (ped->pLpkEditCallout) { pText = Edit_Lock(ped); textExtent = ped->pLpkEditCallout->EditIchToXY((PED0)ped, hdc, pText, ped->cch, ich); Edit_Unlock(ped); return textExtent; } // // Check if we are adding lots and lots of chars. A paste for example could // cause this and GetTextExtents could overflow on this. // cchDiff = (int)ich - (int)ped->ichScreenStart; if (cchDiff > 1000) { return (30000); } else if (cchDiff < -1000) { return (-30000); } if (ped->format != ES_LEFT) { return (ped->rcFmt.left + EditSL_CalcXOffsetSpecial(ped, hdc, ich)); } // // Caret position /w DBCS text, we can not optimize... // if (ped->fNonPropFont && !ped->fDBCS) { return (ped->rcFmt.left + cchDiff*ped->aveCharWidth); } // // Check if password hidden chars are being used. // if (ped->charPasswordChar) { return ( ped->rcFmt.left + cchDiff*ped->cPasswordCharWidth); } pText = Edit_Lock(ped); if (ped->fAnsi) { if (cchDiff >= 0) { GetTextExtentPointA(hdc, (LPSTR)(pText + ped->ichScreenStart), cchDiff, &size); textExtent = size.cx; // // In case of signed/unsigned overflow since the text extent may be // greater than maxint. This happens with long single line edit // controls. The rect we edit text in will never be greater than 30000 // pixels so we are ok if we just ignore them. // if (textExtent < 0 || textExtent > 31000) { textExtent = 30000; } } else { GetTextExtentPointA(hdc,(LPSTR)(pText + ich), -cchDiff, &size); textExtent = (-1) * size.cx; } } else { if (cchDiff >= 0) { GetTextExtentPointW(hdc, (LPWSTR)(pText + ped->ichScreenStart*sizeof(WCHAR)), cchDiff, &size); textExtent = size.cx; // // In case of signed/unsigned overflow since the text extent may be // greater than maxint. This happens with long single line edit // controls. The rect we edit text in will never be greater than 30000 // pixels so we are ok if we just ignore them. // if (textExtent < 0 || textExtent > 31000) { textExtent = 30000; } } else { GetTextExtentPointW(hdc,(LPWSTR)(pText + ich*sizeof(WCHAR)), -cchDiff, &size); textExtent = (-1) * size.cx; } } Edit_Unlock(ped); return (ped->rcFmt.left + textExtent - (textExtent ? ped->charOverhang : 0)); } //---------------------------------------------------------------------------// // // EditSL_SetSelection AorW // // Sets the PED to have the new selection specified. // VOID EditSL_SetSelection(PED ped, ICH ichSelStart, ICH ichSelEnd) { HDC hdc = Edit_GetDC(ped, FALSE); if (ichSelStart == 0xFFFFFFFF) { // // Set no selection if we specify -1 // ichSelStart = ichSelEnd = ped->ichCaret; } // // Bounds ichSelStart, ichSelEnd are checked in EditSL_ChangeSelection... // EditSL_ChangeSelection(ped, hdc, ichSelStart, ichSelEnd); // // Put the caret at the end of the selected text // ped->ichCaret = ped->ichMaxSel; EditSL_SetCaretPosition(ped, hdc); // // We may need to scroll the text to bring the caret into view... // EditSL_ScrollText(ped, hdc); Edit_ReleaseDC(ped, hdc, FALSE); } //---------------------------------------------------------------------------// VOID EditSL_GetClipRect(PED ped, HDC hdc, ICH ichStart, int iCount, LPRECT lpClipRect) { int iStCount; PSTR pText; if (ped->pLpkEditCallout) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_GetClipRect - Error - Invalid call with language pack loaded"); memset(lpClipRect, 0, SIZEOF(RECT)); return; } CopyRect(lpClipRect, &ped->rcFmt); pText = Edit_Lock(ped); // // Calculates the starting pos for this piece of text // if ((iStCount = (int)(ichStart - ped->ichScreenStart)) > 0) { if (ped->format == ES_LEFT) { lpClipRect->left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { // // Reset the values to visible portions // iCount -= (ped->ichScreenStart - ichStart); ichStart = ped->ichScreenStart; } if (ped->format != ES_LEFT) { lpClipRect->left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); } if (iCount < 0) { // // This is not in the visible area of the edit control, so return // an empty rect. // SetRectEmpty(lpClipRect); Edit_Unlock(ped); return; } if (ped->charPasswordChar) { lpClipRect->right = lpClipRect->left + ped->cPasswordCharWidth * iCount; } else { SIZE size; if ( ped->fAnsi) { GetTextExtentPointA(hdc, pText + ichStart, iCount, &size); } else { GetTextExtentPointW(hdc, ((LPWSTR)pText) + ichStart, iCount, &size); } lpClipRect->right = lpClipRect->left + size.cx - ped->charOverhang; } Edit_Unlock(ped); } //---------------------------------------------------------------------------// // // EditSL_LpkEditDrawText // // lpk!EditDrawText always sets the BkMode for single line edits to OPAQUE. // This causes painting problems for read-only edits in property sheets. // Unfortunately, lpk.dll can't be changed since it would break the user32 // edit, so I'm faking lpk!EditDrawText into thinking this isn't a single // line edit. // __inline VOID EditSL_LpkEditDrawText(PED ped, HDC hdc, PSTR pText) { BOOL fSingleSave; fSingleSave = ped->fSingle; ped->fSingle = FALSE; ped->pLpkEditCallout->EditDrawText((PED0)ped, hdc, pText, ped->cch, ped->ichMinSel, ped->ichMaxSel, ped->rcFmt.top); ped->fSingle = fSingleSave; } //---------------------------------------------------------------------------// // // EditSL_ChangeSelection AorW // // Changes the current selection to have the specified starting and // ending values. Properly highlights the new selection and unhighlights // anything deselected. If NewMinSel and NewMaxSel are out of order, we swap // them. Doesn't update the caret position. // VOID EditSL_ChangeSelection(PED ped, HDC hdc, ICH ichNewMinSel, ICH ichNewMaxSel) { ICH temp; ICH ichOldMinSel; ICH ichOldMaxSel; if (ichNewMinSel > ichNewMaxSel) { temp = ichNewMinSel; ichNewMinSel = ichNewMaxSel; ichNewMaxSel = temp; } ichNewMinSel = min(ichNewMinSel, ped->cch); ichNewMaxSel = min(ichNewMaxSel, ped->cch); // // To avoid position to half of DBCS, check and ajust position if necessary // // We check ped->fDBCS and ped->fAnsi though Edit_AdjustIch checks these bits. // We're worrying about the overhead of EcLock and EcUnlock. // if (ped->fDBCS && ped->fAnsi) { PSTR pText; pText = Edit_Lock(ped); ichNewMinSel = Edit_AdjustIch( ped, pText, ichNewMinSel ); ichNewMaxSel = Edit_AdjustIch( ped, pText, ichNewMaxSel ); Edit_Unlock(ped); } // // Preserve the Old selection // ichOldMinSel = ped->ichMinSel; ichOldMaxSel = ped->ichMaxSel; // // Set new selection // ped->ichMinSel = ichNewMinSel; ped->ichMaxSel = ichNewMaxSel; // // We will find the intersection of current selection rectangle with the new // selection rectangle. We will then invert the parts of the two rectangles // not in the intersection. // if (IsWindowVisible(ped->hwnd) && (ped->fFocus || ped->fNoHideSel)) { SELBLOCK Blk[2]; int i; RECT rc; if (ped->fFocus) { HideCaret(ped->hwnd); } if (ped->pLpkEditCallout) { // // The language pack handles display while complex script support present // PSTR pText; HBRUSH hbr = NULL; BOOL fNeedDelete = FALSE; // // Give user a chance to manipulate the DC // hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); FillRect(hdc, &ped->rcFmt, hbr); pText = Edit_Lock(ped); EditSL_LpkEditDrawText(ped, hdc, pText); Edit_Unlock(ped); if (hbr && fNeedDelete) { DeleteObject(hbr); } } else { Blk[0].StPos = ichOldMinSel; Blk[0].EndPos = ichOldMaxSel; Blk[1].StPos = ped->ichMinSel; Blk[1].EndPos = ped->ichMaxSel; if (Edit_CalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel, (LPSELBLOCK)&Blk[0], (LPSELBLOCK)&Blk[1])) { // // Paint the rectangles where selection has changed. // Paint both Blk[0] and Blk[1], if they exist. // for (i = 0; i < 2; i++) { if (Blk[i].StPos != 0xFFFFFFFF) { EditSL_GetClipRect(ped, hdc, Blk[i].StPos, Blk[i].EndPos - Blk[i].StPos, (LPRECT)&rc); EditSL_DrawLine(ped, hdc, rc.left, rc.right, Blk[i].StPos, Blk[i].EndPos - Blk[i].StPos, ((Blk[i].StPos >= ped->ichMinSel) && (Blk[i].StPos < ped->ichMaxSel))); } } } } // // Update caret. // EditSL_SetCaretPosition(ped, hdc); if (ped->fFocus) { ShowCaret(ped->hwnd); } } } //---------------------------------------------------------------------------// // // EditSL_DrawLine() // // This draws the line starting from ichStart, iCount number of characters; // fSelStatus is TRUE if we're to draw the text as selected. // VOID EditSL_DrawLine(PED ped, HDC hdc, int xClipStPos, int xClipEndPos, ICH ichStart, int iCount, BOOL fSelStatus) { RECT rc; RECT rcClip; PSTR pText; DWORD rgbSaveBk; DWORD rgbSaveText; DWORD wSaveBkMode; int iStCount; ICH ichNewStart; HBRUSH hbrBack = NULL; BOOL fNeedDelete = FALSE; HRESULT hr; if (ped->pLpkEditCallout) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_DrawLine - Error - Invalid call with language pack loaded"); return; } // // Anything to draw? // // PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are // slightly different. if (xClipStPos >= xClipEndPos || !IsWindowVisible(ped->hwnd) ) { return; } if (ped->fAnsi && ped->fDBCS) { PSTR pT,pTOrg; int iTCount; pText = Edit_Lock(ped); ichNewStart = 0; if (ichStart > 0) { pT = pText + ichStart; ichNewStart = ichStart; while (ichNewStart && (ichStart - ichNewStart < ped->wMaxNegCcharPos)) { pT = Edit_AnsiPrev(ped, pText, pT); ichNewStart = (ICH)(pT - pText); if (!ichNewStart) { break; } } // // B#16152 - win95. // In case of T2, SLE always set an additional margin // to erase a character (iCount == 0 case), using aveCharWidth. // It erases unexpected an extra char if we don't use ichNewStart // and it happens when wMaxNegCcharPos == 0. // if (ped->wMaxNegCcharPos == 0 && iCount == 0) { pT = Edit_AnsiPrev(ped, pText, pT); ichNewStart = (ICH)(pT - pText); } } iTCount = 0; if (ichStart + iCount < ped->cch) { pTOrg = pT = pText + ichStart + iCount; while ((iTCount < (int)ped->wMaxNegAcharPos) && (ichStart + iCount + iTCount < ped->cch)) { pT = Edit_AnsiNext(ped, pT); iTCount = (int)(pT - pTOrg); } } Edit_Unlock(ped); iCount = (int)(min(ichStart+iCount+iTCount, ped->cch) - ichNewStart); } else { // // Reset ichStart to take care of the negative C widths // ichNewStart = max((int)(ichStart - ped->wMaxNegCcharPos), 0); // // Reset ichCount to take care of the negative C and A widths // iCount = (int)(min(ichStart+iCount+ped->wMaxNegAcharPos, ped->cch) - ichNewStart); } ichStart = ichNewStart; // // Reset ichStart and iCount to the first one visible on the screen // if (ichStart < ped->ichScreenStart) { if (ichStart+iCount < ped->ichScreenStart) { return; } iCount -= (ped->ichScreenStart-ichStart); ichStart = ped->ichScreenStart; } CopyRect(&rc, &ped->rcFmt); // // Set the drawing rectangle // rcClip.left = xClipStPos; rcClip.right = xClipEndPos; rcClip.top = rc.top; rcClip.bottom = rc.bottom; // // Set the proper clipping rectangle // Edit_SetClip(ped, hdc, TRUE); pText = Edit_Lock(ped); // // Calculate the starting pos for this piece of text // if (ped->format == ES_LEFT) { if (iStCount = (int)(ichStart - ped->ichScreenStart)) { rc.left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { rc.left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); } // // Set the background mode before calling NtUserGetControlBrush so that the app // can change it to TRANSPARENT if it wants to. // SetBkMode(hdc, OPAQUE); hr = E_FAIL; #ifdef _USE_DRAW_THEME_TEXT_ if ( ped->hTheme ) { INT iState; INT iProp; COLORREF clrBk; COLORREF clrText; iState = fSelStatus ? ETS_SELECTED : Edit_GetStateId(ped); iProp = fSelStatus ? TMT_HIGHLIGHT : TMT_FILLCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrBk); if ( SUCCEEDED(hr) ) { iProp = fSelStatus ? TMT_HIGHLIGHTTEXT : TMT_TEXTCOLOR; hr = GetThemeColor(ped->hTheme, EP_EDITTEXT, iState, iProp, &clrText); if ( SUCCEEDED(hr) ) { hbrBack = CreateSolidBrush(clrBk); fNeedDelete = TRUE; rgbSaveBk = SetBkColor(hdc, clrBk); rgbSaveText = SetTextColor(hdc, clrText); } } } #endif // _USE_DRAW_THEME_TEXT_ if ( !ped->hTheme || FAILED(hr) ) { if (fSelStatus) { // // if we're not themed or we are themed but failed // to get the highlight and highlighttext colors // // use normal colors // hbrBack = GetSysColorBrush(COLOR_HIGHLIGHT); if (hbrBack == NULL) { goto sldl_errorexit; } rgbSaveBk = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); rgbSaveText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { // // We always want to send this so that the app has a chance to muck // with the DC. // // Note that ReadOnly and Disabled edit fields are drawn as "static" // instead of as "active." // hbrBack = Edit_GetBrush(ped, hdc, &fNeedDelete); rgbSaveBk = GetBkColor(hdc); rgbSaveText = GetTextColor(hdc); } } // // Erase the rectangular area before text is drawn. Note that we inflate // the rect by 1 so that the selection color has a one pixel border around // the text. // InflateRect(&rcClip, 0, 1); FillRect(hdc, &rcClip, hbrBack); InflateRect(&rcClip, 0, -1); if (ped->charPasswordChar) { wSaveBkMode = SetBkMode(hdc, TRANSPARENT); for (iStCount = 0; iStCount < iCount; iStCount++) { if ( ped->fAnsi ) { ExtTextOutA(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip, (LPSTR)&ped->charPasswordChar, 1, NULL); } else { ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip, (LPWSTR)&ped->charPasswordChar, 1, NULL); } rc.left += ped->cPasswordCharWidth; } SetBkMode(hdc, wSaveBkMode); } else { if ( ped->fAnsi ) { ExtTextOutA(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip, pText+ichStart,iCount, NULL); } else { ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &rcClip, ((LPWSTR)pText)+ichStart,iCount, NULL); } } SetTextColor(hdc, rgbSaveText); SetBkColor(hdc, rgbSaveBk); if (hbrBack && fNeedDelete) { DeleteObject(hbrBack); } sldl_errorexit: Edit_Unlock(ped); } //---------------------------------------------------------------------------// // // EditSL_GetBlkEnd AorW // // Given a Starting point and and end point, this function return s whether the // first few characters fall inside or outside the selection block and if so, // howmany characters? // INT EditSL_GetBlkEnd(PED ped, ICH ichStart, ICH ichEnd, BOOL *lpfStatus) { *lpfStatus = FALSE; if (ichStart >= ped->ichMinSel) { if (ichStart >= ped->ichMaxSel) { return (ichEnd - ichStart); } *lpfStatus = TRUE; return (min(ichEnd, ped->ichMaxSel) - ichStart); } return (min(ichEnd, ped->ichMinSel) - ichStart); } //---------------------------------------------------------------------------// // // EditSL_DrawCueBannerText (Unicode Only!) // // This function is called by EditSL_DrawText to display the cue banner text in // the edit box. // // Note: // May need to call pLpkEditCallout to support complex scripts. // VOID EditSL_DrawCueBannerText(PED ped, HDC hdc, RECT rc) { // // Draw the overlay of the cue banner text. // Only draw this text if: // 1. has cue banner text to display // 2. the edit box is empty, // 3. does not have focus, // 4. is not disabled // 5. and is not read only // if (ped->pszCueBannerText && ped->cch == 0 && !ped->fFocus && !ped->fDisabled && !ped->fReadOnly) { COLORREF crOldColor; UINT iOldAlign; // // Setup the font to be light gray // NOTE: Should this be read from the theme manager? // crOldColor = SetTextColor(hdc, GetSysColor(COLOR_BTNSHADOW)); // // Setup the alignment for the text to display. // We will match our alignment with the alignment that is // actually used for text in the edit control // switch (ped->format) { case ES_LEFT: iOldAlign = SetTextAlign(hdc, TA_LEFT); break; case ES_RIGHT: iOldAlign = SetTextAlign(hdc, TA_RIGHT); break; case ES_CENTER: iOldAlign = SetTextAlign(hdc, TA_CENTER); break; } // Draw the text to the box: ExtTextOutW(hdc, rc.left, rc.top, ETO_CLIPPED, &(ped->rcFmt), ped->pszCueBannerText, // Text wcslen(ped->pszCueBannerText), // Size of text NULL); // // Reset the alignment // SetTextAlign(hdc, iOldAlign); // // Reset the color back: // SetTextColor(hdc, crOldColor); } } //---------------------------------------------------------------------------// // // EditSL_DrawText AorW // // Draws text for a single line edit control in the rectangle // specified by ped->rcFmt. If ichStart == 0, starts drawing text at the left // side of the window starting at character index ped->ichScreenStart and draws // as much as will fit. If ichStart > 0, then it appends the characters // starting at ichStart to the end of the text showing in the window. (ie. We // are just growing the text length and keeping the left side // (ped->ichScreenStart to ichStart characters) the same. Assumes the hdc came // from Edit_GetDC so that the caret and such are properly hidden. // VOID EditSL_DrawText(PED ped, HDC hdc, ICH ichStart) { ICH cchToDraw; RECT rc; PSTR pText; BOOL fSelStatus; int iCount, iStCount; ICH ichEnd; BOOL fNoSelection; BOOL fCalcRect; BOOL fDrawLeftMargin = FALSE; BOOL fDrawEndOfLineStrip = FALSE; SIZE size; HBRUSH hbr = NULL; BOOL fNeedDelete = FALSE; // // PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are // slightly different. // if (!IsWindowVisible(ped->hwnd)) { return; } if (ped->pLpkEditCallout) { // // The language pack handles display while complex script support present // // // Give user a chance to manipulate the DC // hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); pText = Edit_Lock(ped); EditSL_LpkEditDrawText(ped, hdc, pText); Edit_Unlock(ped); EditSL_SetCaretPosition(ped, hdc); if (hbr && fNeedDelete) { DeleteObject(hbr); } return; } // // When drawing the entire visible content of special-aligned sle // erase the view. // if (ped->format != ES_LEFT && ichStart == 0) { hbr = Edit_GetBrush(ped, hdc, &fNeedDelete); FillRect(hdc, &ped->rcFmt, hbr); if (hbr && fNeedDelete) { DeleteObject(hbr); } } pText = Edit_Lock(ped); if (ichStart < ped->ichScreenStart) { #if DBG ICH ichCompare = Edit_AdjustIch(ped, pText, ped->ichScreenStart); UserAssert(ichCompare == ped->ichScreenStart); #endif ichStart = ped->ichScreenStart; } else if (ped->fDBCS && ped->fAnsi) { // // If ichStart stays on trailing byte of DBCS, we have to // adjust it. // ichStart = Edit_AdjustIch(ped, pText, ichStart); } CopyRect((LPRECT)&rc, (LPRECT)&ped->rcFmt); // // Find out how many characters will fit on the screen so that we don't do // any needless drawing. // cchToDraw = Edit_CchInWidth(ped, hdc, (LPSTR)(pText + ped->ichScreenStart * ped->cbChar), ped->cch - ped->ichScreenStart, rc.right - rc.left, TRUE); ichEnd = ped->ichScreenStart + cchToDraw; // // There is no selection if, // 1. MinSel and MaxSel are equal OR // 2. (This has lost the focus AND Selection is to be hidden) // fNoSelection = ((ped->ichMinSel == ped->ichMaxSel) || (!ped->fFocus && !ped->fNoHideSel)); if (ped->format == ES_LEFT) { if (iStCount = (int)(ichStart - ped->ichScreenStart)) { rc.left += EditSL_CalcXOffsetLeft(ped, hdc, ichStart); } } else { rc.left += EditSL_CalcXOffsetSpecial(ped, hdc, ichStart); } // // If this is the begining of the whole line, we may have to draw a blank // strip at the begining. // if ((ichStart == 0) && ped->wLeftMargin) { fDrawLeftMargin = TRUE; } // // If there is nothing to draw, that means we need to draw the end of // line strip, which erases the last character. // if (ichStart == ichEnd) { fDrawEndOfLineStrip = TRUE; rc.left -= ped->wLeftMargin; } while (ichStart < ichEnd) { fCalcRect = TRUE; if (fNoSelection) { fSelStatus = FALSE; iCount = ichEnd - ichStart; } else { if (fDrawLeftMargin) { iCount = 0; fSelStatus = FALSE; fCalcRect = FALSE; rc.right = rc.left; } else { iCount = EditSL_GetBlkEnd(ped, ichStart, ichEnd, (BOOL *)&fSelStatus); } } if (ichStart+iCount == ichEnd) { if (fSelStatus) { fDrawEndOfLineStrip = TRUE; } else { rc.right = ped->rcFmt.right + ped->wRightMargin; fCalcRect = FALSE; } } if (fCalcRect) { if (ped->charPasswordChar) { rc.right = rc.left + ped->cPasswordCharWidth * iCount; } else { if ( ped->fAnsi ) { GetTextExtentPointA(hdc, pText + ichStart, iCount, &size); } else { GetTextExtentPointW(hdc, ((LPWSTR)pText) + ichStart, iCount, &size); } rc.right = rc.left + size.cx; // // The extent is equal to the advance width. So for TrueType fonts // we need to take care of Neg A and C. For non TrueType, the extent // includes the overhang. // If drawing the selection, draw only the advance width // if (fSelStatus) { rc.right -= ped->charOverhang; } else if (ped->fTrueType) { rc.right += ped->wMaxNegC; if (iStCount > 0) { rc.right += ped->wMaxNegA; iStCount = 0; } } } } if (fDrawLeftMargin) { fDrawLeftMargin = FALSE; rc.left -= ped->wLeftMargin; if (rc.right < rc.left) { rc.right = rc.left; } } EditSL_DrawLine(ped, hdc, rc.left, rc.right, ichStart, iCount, fSelStatus); ichStart += iCount; rc.left = rc.right; // // If we're going to draw the selection, adjust rc.left // to include advance width of the selected text // For non TT fonts, ped->wMaxNegC equals ped->charOverhang // if (!fSelStatus && (iCount != 0) && (ichStart < ichEnd)) { rc.left -= ped->wMaxNegC; } } Edit_Unlock(ped); // // Check if anything to be erased on the right hand side // if (fDrawEndOfLineStrip && (rc.left < (rc.right = (ped->rcFmt.right+ped->wRightMargin)))) { EditSL_DrawLine(ped, hdc, rc.left, rc.right, ichStart, 0, FALSE); } EditSL_SetCaretPosition(ped, hdc); // // Call the function to display the cue banner text into the edit box // EditSL_DrawCueBannerText(ped, hdc, rc); } //---------------------------------------------------------------------------// // // EditSL_ScrollText AorW // // Scrolls the text to bring the caret into view. If the text is // scrolled, the current selection is unhighlighted. Returns TRUE if the text // is scrolled else return s false. // BOOL EditSL_ScrollText(PED ped, HDC hdc) { PSTR pTextScreenStart; ICH scrollAmount; ICH newScreenStartX = ped->ichScreenStart; ICH cch; BOOLEAN fAdjustNext = FALSE; if (!ped->fAutoHScroll) { return FALSE; } if (ped->pLpkEditCallout) { BOOL fChanged; // // With complex script glyph reordering, use lpk to do horz scroll // pTextScreenStart = Edit_Lock(ped); fChanged = ped->pLpkEditCallout->EditHScroll((PED0)ped, hdc, pTextScreenStart); Edit_Unlock(ped); if (fChanged) { EditSL_DrawText(ped, hdc, 0); } return fChanged; } // // Calculate the new starting screen position // if (ped->ichCaret <= ped->ichScreenStart) { // // Caret is to the left of the starting text on the screen we must // scroll the text backwards to bring it into view. Watch out when // subtracting unsigned numbers when we have the possibility of going // negative. // pTextScreenStart = Edit_Lock(ped); scrollAmount = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->ichCaret, (ped->rcFmt.right - ped->rcFmt.left) / 4, FALSE); newScreenStartX = ped->ichCaret - scrollAmount; Edit_Unlock(ped); } else if (ped->ichCaret != ped->ichScreenStart) { pTextScreenStart = Edit_Lock(ped); pTextScreenStart += ped->ichScreenStart * ped->cbChar; cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->ichCaret - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE); if (cch < ped->ichCaret - ped->ichScreenStart) { fAdjustNext = TRUE; // // Scroll Forward 1/4 -- if that leaves some empty space // at the end, scroll back enough to fill the space // newScreenStartX = ped->ichCaret - (3 * cch / 4); cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE); if (newScreenStartX > (ped->cch - cch)) { newScreenStartX = ped->cch - cch; } } else if (ped->format != ES_LEFT) { cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, FALSE); // // Scroll the text hidden behind the left border back // into view. // if (ped->ichScreenStart == ped->cch - cch) { pTextScreenStart -= ped->ichScreenStart * ped->cbChar; cch = Edit_CchInWidth(ped, hdc, (LPSTR)pTextScreenStart, ped->cch, ped->rcFmt.right - ped->rcFmt.left, FALSE); newScreenStartX = ped->cch - cch; } } Edit_Unlock(ped); } // // Adjust newScreenStartX // if (ped->fAnsi && ped->fDBCS) { newScreenStartX = (fAdjustNext ? Edit_AdjustIchNext : Edit_AdjustIch)(ped, Edit_Lock(ped), newScreenStartX); Edit_Unlock(ped); } if (ped->ichScreenStart != newScreenStartX) { // // Check if we have to wipe out the left margin // if (ped->wLeftMargin && (ped->ichScreenStart == 0)) { RECT rc; HBRUSH hBrush = NULL; BOOL fNeedDelete = FALSE; hBrush = Edit_GetBrush(ped, hdc, &fNeedDelete); CopyRect(&rc, &ped->rcFmt); InflateRect(&rc, 0, 1); rc.right = rc.left; rc.left -= ped->wLeftMargin; FillRect(hdc, &rc, hBrush); if (hBrush && fNeedDelete) { DeleteObject(hBrush); } } ped->ichScreenStart = newScreenStartX; EditSL_DrawText(ped, hdc, 0); // // Caret pos is set by EditSL_DrawText(). // return TRUE; } return FALSE; } //---------------------------------------------------------------------------// // // EditSL_InsertText AorW // // Adds up to cchInsert characters from lpText to the ped starting at // ichCaret. If the ped only allows a maximum number of characters, then we // will only add that many characters to the ped and send a EN_MAXTEXT // notification code to the parent of the ec. Also, if !fAutoHScroll, then we // only allow as many chars as will fit in the client rectangle. The number of // characters actually added is return ed (could be 0). If we can't allocate // the required space, we notify the parent with EN_ERRSPACE and no characters // are added. // ICH EditSL_InsertText(PED ped, LPSTR lpText, ICH cchInsert) { HDC hdc; PSTR pText; ICH cchInsertCopy = cchInsert; ICH cchT; int textWidth; SIZE size; // // First determine exactly how many characters from lpText we can insert // into the ped. // if( ped->cchTextMax <= ped->cch) { cchInsert = 0; } else { if (!ped->fAutoHScroll) { pText = Edit_Lock(ped); hdc = Edit_GetDC(ped, TRUE); cchInsert = min(cchInsert, (unsigned)(ped->cchTextMax - ped->cch)); if (ped->charPasswordChar) { textWidth = ped->cch * ped->cPasswordCharWidth; } else { if (ped->fAnsi) { GetTextExtentPointA(hdc, (LPSTR)pText, ped->cch, &size); } else { GetTextExtentPointW(hdc, (LPWSTR)pText, ped->cch, &size); } textWidth = size.cx; } cchT = Edit_CchInWidth(ped, hdc, lpText, cchInsert, ped->rcFmt.right - ped->rcFmt.left - textWidth, TRUE); cchInsert = min(cchInsert, cchT); Edit_Unlock(ped); Edit_ReleaseDC(ped, hdc, TRUE); } else { cchInsert = min((unsigned)(ped->cchTextMax - ped->cch), cchInsert); } } // // Now try actually adding the text to the ped // if (cchInsert && !Edit_InsertText(ped, lpText, &cchInsert)) { Edit_NotifyParent(ped, EN_ERRSPACE); return 0; } if (cchInsert) { ped->fDirty = TRUE; } if (cchInsert < cchInsertCopy) { // // Notify parent that we couldn't insert all the text requested // Edit_NotifyParent(ped, EN_MAXTEXT); } // // Update selection extents and the caret position. Note that Edit_InsertText // updates ped->ichCaret, ped->ichMinSel, and ped->ichMaxSel to all be after // the inserted text. // return cchInsert; } //---------------------------------------------------------------------------// // // EditSL_PasteText AorW // // Pastes a line of text from the clipboard into the edit control // starting at ped->ichMaxSel. Updates ichMaxSel and ichMinSel to point to // the end of the inserted text. Notifies the parent if space cannot be // allocated. Returns how many characters were inserted. // ICH EditSL_PasteText(PED ped) { HANDLE hData; LPSTR lpchClip; ICH cchAdded = 0; ICH clipLength; if (!OpenClipboard(ped->hwnd)) { goto PasteExitNoCloseClip; } hData = GetClipboardData(ped->fAnsi ? CF_TEXT : CF_UNICODETEXT); if (!hData || (GlobalFlags(hData) == GMEM_INVALID_HANDLE)) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_PasteText(): couldn't get a valid handle(%x)", hData); goto PasteExit; } lpchClip = GlobalLock(hData); if (lpchClip == NULL) { TraceMsg(TF_STANDARD, "UxEdit: EditSL_PasteText(): USERGLOBALLOCK(%x) failed.", hData); goto PasteExit; } if (ped->fAnsi) { LPSTR lpchClip2 = lpchClip; // // Find the first carrage return or line feed. Just add text to that point. // clipLength = (UINT)strlen(lpchClip); for (cchAdded = 0; cchAdded < clipLength; cchAdded++) { if (*lpchClip2++ == 0x0D) { break; } } } else { LPWSTR lpwstrClip2 = (LPWSTR)lpchClip; // // Find the first carrage return or line feed. Just add text to that point. // clipLength = (UINT)wcslen((LPWSTR)lpchClip); for (cchAdded = 0; cchAdded < clipLength; cchAdded++) { if (*lpwstrClip2++ == 0x0D) { break; } } } // // Insert the text (EditSL_InsertText checks line length) // cchAdded = EditSL_InsertText(ped, lpchClip, cchAdded); GlobalUnlock(hData); PasteExit: CloseClipboard(); PasteExitNoCloseClip: return cchAdded; } //---------------------------------------------------------------------------// // // EditSL_ReplaceSel AorW // // Replaces the text in the current selection with the given text. // VOID EditSL_ReplaceSel(PED ped, LPSTR lpText) { UINT cchText; // // Delete text, putting it into the clean undo buffer. // Edit_EmptyUndo(Pundo(ped)); Edit_DeleteText(ped); // // B#3356 // Some apps do "clear" by selecting all of the text, then replacing it // with "", in which case EditSL_InsertText() will return 0. But that // doesn't mean failure... // if ( ped->fAnsi ) { cchText = strlen(lpText); } else { cchText = wcslen((LPWSTR)lpText); } if (cchText) { BOOL fFailed; UNDO undo; HWND hwndSave; // // Save undo buffer, but DO NOT CLEAR IT! // Edit_SaveUndo(Pundo(ped), &undo, FALSE); hwndSave = ped->hwnd; fFailed = (BOOL) !EditSL_InsertText(ped, lpText, cchText); if (!IsWindow(hwndSave)) { return; } if (fFailed) { // // UNDO the previous edit. // Edit_SaveUndo(&undo, Pundo(ped), FALSE); EditSL_Undo(ped); return; } } // // Success. So update the display // Edit_NotifyParent(ped, EN_UPDATE); // PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are // slightly different. if (IsWindowVisible(ped->hwnd)) { HDC hdc; hdc = Edit_GetDC(ped, FALSE); if (!EditSL_ScrollText(ped, hdc)) { EditSL_DrawText(ped, hdc, 0); } Edit_ReleaseDC(ped, hdc, FALSE); } Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } //---------------------------------------------------------------------------// // // EditSL_Char AorW // // Handles character input // VOID EditSL_Char(PED ped, DWORD keyValue) { HDC hdc; WCHAR keyPress; BOOL updateText = FALSE; HWND hwndSave = ped->hwnd; int InsertTextLen = 1; int DBCSkey; if (ped->fAnsi) { keyPress = LOBYTE(keyValue); } else { keyPress = LOWORD(keyValue); } if (ped->fMouseDown || (ped->fReadOnly && keyPress != 3)) { // // Don't do anything if we are in the middle of a mousedown deal or if // this is a read only edit control, with exception of allowing // ctrl-C in order to copy to the clipboard. // return; } if (g_fIMMEnabled) { Edit_InOutReconversionMode(ped, FALSE); } switch (keyPress) { case VK_BACK: DeleteSelection: if (Edit_DeleteText(ped)) { updateText = TRUE; } break; default: if (keyPress >= TEXT(' ')) { // // If this is in [a-z],[A-Z] and we are an ES_NUMBER // edit field, bail. // if (ped->f40Compat && (GET_STYLE(ped) & ES_NUMBER)) { if (!Edit_IsCharNumeric(ped, keyPress)) { Edit_ShowBalloonTipWrap(ped->hwnd, IDS_NUMERIC_TITLE, IDS_NUMERIC_MSG, TTI_ERROR); goto IllegalChar; } } goto DeleteSelection; } break; } switch (keyPress) { case 3: // // CTRL-C Copy // SendMessage(ped->hwnd, WM_COPY, 0, 0L); return; case VK_BACK: // // Delete any selected text or delete character left if no sel // if (!updateText && ped->ichMinSel) { // // There was no selection to delete so we just delete character // left if available // // Calling PrevIch rather than just doing a decrement for VK_BACK // ped->ichMinSel = Edit_PrevIch( ped, NULL, ped->ichMinSel); Edit_DeleteText(ped); updateText = TRUE; } break; case 22: // // CTRL-V Paste // SendMessage(ped->hwnd, WM_PASTE, 0, 0L); return; case 24: // // CTRL-X Cut // if (ped->ichMinSel == ped->ichMaxSel) { goto IllegalChar; } SendMessage(ped->hwnd, WM_CUT, 0, 0L); return; case 26: // // CTRL-Z Undo // SendMessage(ped->hwnd, EM_UNDO, 0, 0L); return; case VK_RETURN: case VK_ESCAPE: // // If this is an edit control for a combobox and the dropdown list // is visible, forward it up to the combo. // if (ped->listboxHwnd && SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L)) { SendMessage(ped->hwndParent, WM_KEYDOWN, (WPARAM)keyPress, 0L); } else { goto IllegalChar; } return; default: if (keyPress >= 0x1E) { // // 1E,1F are unicode block and segment separators // // // Hide the cursor if typing, if the mouse is captured, do not mess with this // as it is going to desapear forever (no WM_SETCURSOR is sent to restore it // at the first mouse-move) // MCostea #166951 // if (GetCapture() == NULL) { SetCursor(NULL); } if (g_fDBCSEnabled && ped->fAnsi && (Edit_IsDBCSLeadByte(ped,(BYTE)keyPress))) { if ((DBCSkey = DbcsCombine(ped->hwnd, keyPress)) != 0 && EditSL_InsertText(ped,(LPSTR)&DBCSkey, 2) == 2) { InsertTextLen = 2; updateText = TRUE; } else { MessageBeep(0); } } else { InsertTextLen = 1; if (EditSL_InsertText(ped, (LPSTR)&keyPress, 1)) { updateText = TRUE; } else { // // Beep. Since we couldn't add the text // MessageBeep(0); } } } else { IllegalChar: MessageBeep(0); } if (!IsWindow(hwndSave)) { return; } break; } if (updateText) { // // Dirty flag (ped->fDirty) was set when we inserted text // Edit_NotifyParent(ped, EN_UPDATE); hdc = Edit_GetDC(ped, FALSE); if (!EditSL_ScrollText(ped, hdc)) { if (ped->format == ES_LEFT) { // // Call EditSL_DrawText with correct ichStart // EditSL_DrawText(ped, hdc, max(0, (int)(ped->ichCaret - InsertTextLen - ped->wMaxNegCcharPos))); } else { // // We can't just draw from ichStart because string may have // shifted because of alignment. // EditSL_DrawText(ped, hdc, 0); } } Edit_ReleaseDC(ped, hdc, FALSE); Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } } //---------------------------------------------------------------------------// // // EditSL_MoveSelectionRestricted AorW // // Moves the selection like Edit_MoveSelection, but also obeys limitations // imposed by some languages such as Thai, where the cursor cannot stop // between a character and it's attached vowel or tone marks. // // Only called if the language pack is loaded. // ICH EditSL_MoveSelectionRestricted(PED ped, ICH ich, BOOL fLeft) { PSTR pText; HDC hdc; ICH ichResult; pText = Edit_Lock(ped); hdc = Edit_GetDC(ped, TRUE); ichResult = ped->pLpkEditCallout->EditMoveSelection((PED0)ped, hdc, pText, ich, fLeft); Edit_ReleaseDC(ped, hdc, TRUE); Edit_Unlock(ped); return ichResult; } //---------------------------------------------------------------------------// void EditSL_CheckCapsLock(PED ped) { if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) { Edit_ShowBalloonTipWrap(ped->hwnd, IDS_CAPSLOCK_TITLE, IDS_CAPSLOCK_MSG, TTI_WARNING); } } //---------------------------------------------------------------------------// // // EditSL_KeyDown AorW // // Handles cursor movement and other VIRT KEY stuff. keyMods allows // us to make EditSL_KeyDownHandler calls and specify if the modifier keys (shift // and control) are up or down. This is useful for imnplementing the // cut/paste/clear messages for single line edit controls. If keyMods == 0, // we get the keyboard state using GetKeyState(VK_SHIFT) etc. Otherwise, the // bits in keyMods define the state of the shift and control keys. // VOID EditSL_KeyDown(PED ped, DWORD virtKeyCode, int keyMods) { HDC hdc; // // Variables we will use for redrawing the updated text // ICH newMaxSel = ped->ichMaxSel; ICH newMinSel = ped->ichMinSel; // // Flags for drawing the updated text // BOOL updateText = FALSE; BOOL changeSelection = FALSE; // new selection is specified by // newMinSel, newMaxSel // // Comparisons we do often // BOOL MinEqMax = (newMaxSel == newMinSel); BOOL MinEqCar = (ped->ichCaret == newMinSel); BOOL MaxEqCar = (ped->ichCaret == newMaxSel); // // State of shift and control keys. // int scState; // // Combo box support // BOOL fIsListVisible; BOOL fIsExtendedUI; if (ped->fMouseDown) { // // If we are in the middle of a mouse down handler, then don't do // anything. ie. ignore keyboard input. // return; } if (ped->hwndBalloon) { Edit_HideBalloonTip(ped->hwnd); } scState = Edit_GetModKeys(keyMods); switch (virtKeyCode) { case VK_UP: if ( ped->listboxHwnd ) { // // Handle Combobox support // fIsExtendedUI = (BOOL)SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0); fIsListVisible = (BOOL)SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0); if (!fIsListVisible && fIsExtendedUI) { DropExtendedUIListBox: // // Since an extendedui combo box doesn't do anything on f4, we // turn off the extended ui, send the f4 to drop, and turn it // back on again. // SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 0, 0); SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0); SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 1, 0); return; } else { goto SendKeyToListBox; } } // // else fall through // case VK_LEFT: // // If the caret isn't at the beginning, we can move left // if (ped->ichCaret) { // // Get new caret pos. // if (scState & CTRLDOWN) { // // Move caret word left // Edit_Word(ped, ped->ichCaret, TRUE, &ped->ichCaret, NULL); } else { // // Move caret char left // if (ped->pLpkEditCallout) { ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, TRUE); } else { ped->ichCaret = Edit_PrevIch(ped,NULL,ped->ichCaret); } } // // Get new selection // if (scState & SHFTDOWN) { if (MaxEqCar && !MinEqMax) { // // Reduce selection // newMaxSel = ped->ichCaret; UserAssert(newMinSel == ped->ichMinSel); } else { // // Extend selection // newMinSel = ped->ichCaret; } } else { // // Clear selection // newMaxSel = newMinSel = ped->ichCaret; } changeSelection = TRUE; } else { // // If the user tries to move left and we are at the 0th // character and there is a selection, then cancel the // selection. // if ( (ped->ichMaxSel != ped->ichMinSel) && !(scState & SHFTDOWN) ) { changeSelection = TRUE; newMaxSel = newMinSel = ped->ichCaret; } } break; case VK_DOWN: if (ped->listboxHwnd) { // // Handle Combobox support // fIsExtendedUI = (BOOL)SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0); fIsListVisible = (BOOL)SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0); if (!fIsListVisible && fIsExtendedUI) { goto DropExtendedUIListBox; } else { goto SendKeyToListBox; } } // // else fall through // case VK_RIGHT: // // If the caret isn't at the end, we can move right. // if (ped->ichCaret < ped->cch) { // // Get new caret pos. // if (scState & CTRLDOWN) { // // Move caret word right // Edit_Word(ped, ped->ichCaret, FALSE, NULL, &ped->ichCaret); } else { // // Move caret char right // if (ped->pLpkEditCallout) { ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, FALSE); } else { ped->ichCaret = Edit_NextIch(ped,NULL,ped->ichCaret); } } // // Get new selection. // if (scState & SHFTDOWN) { if (MinEqCar && !MinEqMax) { // // Reduce selection // newMinSel = ped->ichCaret; UserAssert(newMaxSel == ped->ichMaxSel); } else { // // Extend selection // newMaxSel = ped->ichCaret; } } else { // // Clear selection // newMaxSel = newMinSel = ped->ichCaret; } changeSelection = TRUE; } else { // // If the user tries to move right and we are at the last // character and there is a selection, then cancel the // selection. // if ( (ped->ichMaxSel != ped->ichMinSel) && !(scState & SHFTDOWN) ) { newMaxSel = newMinSel = ped->ichCaret; changeSelection = TRUE; } } break; case VK_HOME: // // Move caret to top. // ped->ichCaret = 0; // // Update selection. // if (scState & SHFTDOWN) { if (MaxEqCar && !MinEqMax) { // // Reduce selection // newMinSel = ped->ichCaret; newMaxSel = ped->ichMinSel; } else { // // Extend selection // newMinSel = ped->ichCaret; } } else { // // Clear selection // newMaxSel = newMinSel = ped->ichCaret; } changeSelection = TRUE; break; case VK_END: // // Move caret to end. // ped->ichCaret = ped->cch; // // Update selection. // newMaxSel = ped->ichCaret; if (scState & SHFTDOWN) { if (MinEqCar && !MinEqMax) { // // Reduce selection // newMinSel = ped->ichMaxSel; } } else { // // Clear selection // newMinSel = ped->ichCaret; } changeSelection = TRUE; break; case VK_DELETE: if (ped->fReadOnly) { break; } switch (scState) { case NONEDOWN: // // Clear selection. If no selection, delete (clear) character // right. // if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) { // // Move cursor forwards and simulate a backspace. // if (ped->pLpkEditCallout) { ped->ichMinSel = ped->ichCaret; ped->ichMaxSel = ped->ichCaret = EditSL_MoveSelectionRestricted(ped, ped->ichCaret, FALSE); } else { ped->ichCaret = Edit_NextIch(ped,NULL,ped->ichCaret); ped->ichMaxSel = ped->ichMinSel = ped->ichCaret; } EditSL_Char(ped, (UINT)VK_BACK); } if (ped->ichMinSel != ped->ichMaxSel) { EditSL_Char(ped, (UINT)VK_BACK); } break; case SHFTDOWN: // // Send ourself a WM_CUT message if a selection exists. // Otherwise, delete the left character. // if (ped->ichMinSel == ped->ichMaxSel) { UserAssert(!ped->fEatNextChar); EditSL_Char(ped, VK_BACK); } else { SendMessage(ped->hwnd, WM_CUT, 0, 0L); } break; case CTRLDOWN: // // Delete to end of line if no selection else delete (clear) // selection. // if ((ped->ichMaxSel < ped->cch) && (ped->ichMinSel == ped->ichMaxSel)) { // // Move cursor to end of line and simulate a backspace. // ped->ichMaxSel = ped->ichCaret = ped->cch; } if (ped->ichMinSel != ped->ichMaxSel) { EditSL_Char(ped, (UINT)VK_BACK); } break; } // // No need to update text or selection since BACKSPACE message does it // for us. // break; case VK_INSERT: switch (scState) { case CTRLDOWN: // // Copy current selection to clipboard // SendMessage(ped->hwnd, WM_COPY, 0, 0); break; case SHFTDOWN: SendMessage(ped->hwnd, WM_PASTE, 0, 0L); break; } break; case VK_HANJA: // // VK_HANJA support // if ( HanjaKeyHandler( ped ) ) { changeSelection = TRUE; newMinSel = ped->ichCaret; newMaxSel = ped->ichCaret + (ped->fAnsi ? 2 : 1); } break; case VK_CAPITAL: if (GET_STYLE(ped) & ES_PASSWORD) { EditSL_CheckCapsLock(ped); } break; case VK_F4: case VK_PRIOR: case VK_NEXT: // // Send keys to the listbox if we are a part of a combo box. This // assumes the listbox ignores keyup messages which is correct right // now. // SendKeyToListBox: if (ped->listboxHwnd) { // // Handle Combobox support // SendMessage(ped->listboxHwnd, WM_KEYDOWN, virtKeyCode, 0L); return; } } if (changeSelection || updateText) { hdc = Edit_GetDC(ped, FALSE); // // Scroll if needed // EditSL_ScrollText(ped, hdc); if (changeSelection) { EditSL_ChangeSelection(ped, hdc, newMinSel, newMaxSel); } if (updateText) { EditSL_DrawText(ped, hdc, 0); } Edit_ReleaseDC(ped, hdc, FALSE); if (updateText) { Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } } } //---------------------------------------------------------------------------// // // EditSL_MouseToIch AorW // // Returns the closest cch to where the mouse point is. // ICH EditSL_MouseToIch(PED ped, HDC hdc, LPPOINT mousePt) { PSTR pText; int width = mousePt->x; int lastHighWidth, lastLowWidth; SIZE size; ICH cch; ICH cchLo, cchHi; LPSTR lpText; FnGetTextExtentPoint pGetTextExtentPoint; if (ped->pLpkEditCallout) { pText = Edit_Lock(ped); cch = ped->pLpkEditCallout->EditMouseToIch((PED0)ped, hdc, pText, ped->cch, width); Edit_Unlock(ped); return cch; } if (width <= ped->rcFmt.left) { // // Return either the first non visible character or return 0 if at // beginning of text // if (ped->ichScreenStart) { return (ped->ichScreenStart - 1); } else { return 0; } } if (width > ped->rcFmt.right) { pText = Edit_Lock(ped); // // Return last char in text or one plus the last char visible // cch = Edit_CchInWidth(ped, hdc, (LPSTR)(pText + ped->ichScreenStart * ped->cbChar), ped->cch - ped->ichScreenStart, ped->rcFmt.right - ped->rcFmt.left, TRUE) + ped->ichScreenStart; // // This is marked as JAPAN in Win31J. But it should be a DBCS // issue. LiZ -- 5/5/93 // We must check DBCS Lead byte. Because Edit_AdjustIch() pick up Prev Char. // 1993.3.9 by yutakas // if (ped->fAnsi && ped->fDBCS) { if (cch >= ped->cch) { cch = ped->cch; } else { if (Edit_IsDBCSLeadByte(ped,*(pText+cch))) { cch += 2; } else { cch ++; } } Edit_Unlock(ped); return cch; } else { Edit_Unlock(ped); if (cch >= ped->cch) { return (ped->cch); } else { return (cch + 1); } } } if (ped->format != ES_LEFT) { width -= EditSL_CalcXOffsetSpecial(ped, hdc, ped->ichScreenStart); } // // Check if password hidden chars are being used. // if (ped->charPasswordChar) { return min( (DWORD)( (width - ped->rcFmt.left) / ped->cPasswordCharWidth), ped->cch); } if (!ped->cch) { return 0; } pText = Edit_Lock(ped); lpText = pText + ped->ichScreenStart * ped->cbChar; pGetTextExtentPoint = ped->fAnsi ? (FnGetTextExtentPoint)GetTextExtentPointA : (FnGetTextExtentPoint)GetTextExtentPointW; width -= ped->rcFmt.left; // // If the user clicked past the end of the text, return the last character // cchHi = ped->cch - ped->ichScreenStart; pGetTextExtentPoint(hdc, lpText, cchHi, &size); if (size.cx <= width) { cch = cchHi; goto edAdjust; } // // Initialize Binary Search Bounds // cchLo = 0; cchHi ++; lastLowWidth = 0; lastHighWidth = size.cx; // // Binary search for closest char // while (cchLo < cchHi - 1) { cch = (cchHi + cchLo) / 2; pGetTextExtentPoint(hdc, lpText, cch, &size); if (size.cx <= width) { cchLo = cch; lastLowWidth = size.cx; } else { cchHi = cch; lastHighWidth = size.cx; } } // // When the while ends, you can't know the exact position. // Try to see if the mouse pointer was on the farest half // of the char we got and if so, adjust cch. // if (cchLo == cch) { // // Need to compare with lastHighWidth // if ((lastHighWidth - width) < (width - size.cx)) { cch++; } } else { // // Need to compare with lastLowWidth // if ((width - lastLowWidth) < (size.cx - width)) { cch--; } } edAdjust: // // Avoid to point the intermediate of double byte character // cch = Edit_AdjustIch(ped, pText, cch + ped->ichScreenStart); Edit_Unlock(ped); return cch; } //---------------------------------------------------------------------------// VOID EditSL_MouseMotion(PED ped, UINT message, UINT virtKeyDown, LPPOINT mousePt) { DWORD selectionl; DWORD selectionh; BOOL changeSelection; ICH newMaxSel; ICH newMinSel; HDC hdc; ICH mouseIch; LPSTR pText; changeSelection = FALSE; newMinSel = ped->ichMinSel; newMaxSel = ped->ichMaxSel; hdc = Edit_GetDC(ped, FALSE); mouseIch = EditSL_MouseToIch(ped, hdc, mousePt); switch (message) { case WM_LBUTTONDBLCLK: // // if shift key is down, extend selection to word we double clicked on // else clear current selection and select word. // // in DBCS, we have different word breaking. LiZ -- 5/5/93 // In Hangeul Environment we use word selection feature because Hangeul // use SPACE as word break // if (ped->fAnsi && ped->fDBCS) { pText = Edit_Lock(ped) + mouseIch; Edit_Word(ped, mouseIch, (Edit_IsDBCSLeadByte(ped,*pText) && mouseIch < ped->cch) ? FALSE : TRUE, &selectionl, &selectionh); Edit_Unlock(ped); } else { Edit_Word(ped, mouseIch, (mouseIch) ? TRUE : FALSE, &selectionl, &selectionh); } if (!(virtKeyDown & MK_SHIFT)) { // // If shift key isn't down, move caret to mouse point and clear // old selection // newMinSel = selectionl; newMaxSel = ped->ichCaret = selectionh; } else { // // Shiftkey is down so we want to maintain the current selection // (if any) and just extend or reduce it // if (ped->ichMinSel == ped->ichCaret) { newMinSel = ped->ichCaret = selectionl; Edit_Word(ped, newMaxSel, TRUE, &selectionl, &selectionh); } else { newMaxSel = ped->ichCaret = selectionh; Edit_Word(ped, newMinSel, FALSE, &selectionl, &selectionh); } ped->ichMaxSel = ped->ichCaret; } ped->ichStartMinSel = selectionl; ped->ichStartMaxSel = selectionh; goto InitDragSelect; case WM_MOUSEMOVE: // // We know the mouse button's down -- otherwise the OPTIMIZE // test would've failed in EditSL_WndProc and never called // changeSelection = TRUE; // // Extend selection, move caret word right // if (ped->ichStartMinSel || ped->ichStartMaxSel) { // // We're in WORD SELECT mode // BOOL fReverse = (mouseIch <= ped->ichStartMinSel); Edit_Word(ped, mouseIch, !fReverse, &selectionl, &selectionh); if (fReverse) { newMinSel = ped->ichCaret = selectionl; newMaxSel = ped->ichStartMaxSel; } else { newMinSel = ped->ichStartMinSel; newMaxSel = ped->ichCaret = selectionh; } } else if ((ped->ichMinSel == ped->ichCaret) && (ped->ichMinSel != ped->ichMaxSel)) { // // Reduce selection extent // newMinSel = ped->ichCaret = mouseIch; } else { // Extend selection extent newMaxSel = ped->ichCaret=mouseIch; } break; case WM_LBUTTONDOWN: // // If we currently don't have the focus yet, try to get it. // if (!ped->fFocus) { if (!ped->fNoHideSel) { // // Clear the selection before setting the focus so that we // don't get refresh problems and flicker. Doesn't matter // since the mouse down will end up changing it anyway. // ped->ichMinSel = ped->ichMaxSel = ped->ichCaret; } SetFocus(ped->hwnd); // // BOGUS // (1) We should see if SetFocus() succeeds. // (2) We should ignore mouse messages if the first window // ancestor with a caption isn't "active." // // If we are part of a combo box, then this is the first time // the edit control is getting the focus so we just want to // highlight the selection and we don't really want to position // the caret. // if (ped->listboxHwnd) { break; } // // We yield at SetFocus -- text might have changed at that point // update selection and caret info accordingly // FIX for bug # 11743 -- JEFFBOG 8/23/91 // newMaxSel = ped->ichMaxSel; newMinSel = ped->ichMinSel; mouseIch = min(mouseIch, ped->cch); } if (ped->fFocus) { // // Only do this if we have the focus since a clever app may not // want to give us the focus at the SetFocus call above. // if (!(virtKeyDown & MK_SHIFT)) { // // If shift key isn't down, move caret to mouse point and // clear old selection // newMinSel = newMaxSel = ped->ichCaret = mouseIch; } else { // // Shiftkey is down so we want to maintain the current // selection (if any) and just extend or reduce it // if (ped->ichMinSel == ped->ichCaret) { newMinSel = ped->ichCaret = mouseIch; } else { newMaxSel = ped->ichCaret = mouseIch; } } ped->ichStartMinSel = ped->ichStartMaxSel = 0; InitDragSelect: ped->fMouseDown = FALSE; SetCapture(ped->hwnd); ped->fMouseDown = TRUE; changeSelection = TRUE; } break; case WM_LBUTTONUP: if (ped->fMouseDown) { ped->fMouseDown = FALSE; ReleaseCapture(); } break; } if (changeSelection) { EditSL_ScrollText(ped,hdc); EditSL_ChangeSelection(ped, hdc, newMinSel, newMaxSel); } Edit_ReleaseDC(ped, hdc, FALSE); } //---------------------------------------------------------------------------// // // EditSL_Paint AorW // // Handles painting of the edit control window. Draws the border if // necessary and draws the text in its current state. // VOID EditSL_Paint(PED ped, HDC hdc) { RECT rcEdit; HWND hwnd = ped->hwnd; HBRUSH hBrushRemote = NULL; BOOL fNeedDelete = FALSE; HANDLE hOldFont; // // Had to put in hide/show carets. The first one needs to be done before // beginpaint to correctly paint the caret if part is in the update region // and part is out. The second is for 1.03 compatibility. It breaks // micrografix's worksheet edit control if not there. // HideCaret(hwnd); if (IsWindowVisible(hwnd)) { CCDBUFFER db; // // Erase the background since we don't do it in the erasebkgnd message. // GetClientRect(hwnd, &rcEdit); hdc = CCBeginDoubleBuffer(hdc, &rcEdit, &db); #ifdef _USE_DRAW_THEME_TEXT_ if (!ped->hTheme) #endif // _USE_DRAW_THEME_TEXT_ { hBrushRemote = Edit_GetBrush(ped, hdc, &fNeedDelete); if (hBrushRemote) { FillRect(hdc, &rcEdit, hBrushRemote); if (fNeedDelete) { DeleteObject(hBrushRemote); } } if (ped->fFlatBorder) { DrawFrame(hdc, &rcEdit, 1, DF_WINDOWFRAME); } } #ifdef _USE_DRAW_THEME_TEXT_ else { HRESULT hr; INT iStateId = Edit_GetStateId(ped); hr = DrawThemeBackground(ped->hTheme, hdc, EP_EDITTEXT, iStateId, &rcEdit, 0); } #endif // _USE_DRAW_THEME_TEXT_ if (ped->hFont != NULL) { // // We have to select in the font since this may be a subclassed dc // or a begin paint dc which hasn't been initialized with out fonts // like Edit_GetDC does. // hOldFont = SelectObject(hdc, ped->hFont); } EditSL_DrawText(ped, hdc, 0); if (ped->hFont != NULL && hOldFont != NULL) { SelectObject(hdc, hOldFont); } CCEndDoubleBuffer(&db); } ShowCaret(hwnd); } //---------------------------------------------------------------------------// // // EditSL_SetFocus AorW // // Gives the edit control the focus and notifies the parent // EN_SETFOCUS. // VOID EditSL_SetFocus(PED ped) { if (!ped->fFocus) { HDC hdc; UINT cxCaret; ped->fFocus = TRUE; InvalidateRect(ped->hwnd, NULL, TRUE); // // We don't want to muck with the caret since it isn't created. // hdc = Edit_GetDC(ped, TRUE); // // Show the current selection if necessary. // if (!ped->fNoHideSel) { EditSL_DrawText(ped, hdc, 0); } // // Create the caret // SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0); if (ped->pLpkEditCallout) { ped->pLpkEditCallout->EditCreateCaret ((PED0)ped, hdc, cxCaret, ped->lineHeight, 0); } else { CreateCaret(ped->hwnd, (HBITMAP)NULL, cxCaret, ped->lineHeight); } EditSL_SetCaretPosition(ped, hdc); Edit_ReleaseDC(ped, hdc, TRUE); ShowCaret(ped->hwnd); // // check the capslock key // if (GET_STYLE(ped) & ES_PASSWORD) { EditSL_CheckCapsLock(ped); } } // // Notify parent we have the focus // Edit_NotifyParent(ped, EN_SETFOCUS); } //---------------------------------------------------------------------------// // // EditSL_KillFocus // // The edit control loses the focus and notifies the parent via EN_KILLFOCUS. // void EditSL_KillFocus(PED ped, HWND newFocusHwnd) { HWND hwnd = ped->hwnd; if (ped->fFocus) { DestroyCaret(); ped->fFocus = FALSE; // // Do this only if we still have the focus. But we always notify the // parent that we lost the focus whether or not we originally had the // focus. // // Hide the current selection if needed // #ifdef _USE_DRAW_THEME_TEXT_ if ((!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel)) || ped->hTheme) #else if ((!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel))) #endif // _USE_DRAW_THEME_TEXT_ { InvalidateRect(hwnd, NULL, FALSE); } } // // If we aren't a combo box, notify parent that we lost the focus. // if (!ped->listboxHwnd) { Edit_NotifyParent(ped, EN_KILLFOCUS); } else { // // This editcontrol is part of a combo box and is losing the focus. If // the focus is NOT being sent to another control in the combo box // window, then it means the combo box is losing the focus. So we will // notify the combo box of this fact. // if ((newFocusHwnd == NULL) || (!IsChild(ped->hwndParent, newFocusHwnd))) { // // Excel has a slaker in it's midst. They're not using our combo // boxes, but they still expect to get all the internal messages // that we give to OUR comboboxes. And they expect them to be at // the same offset from WM_USER as they were in 3.1. // (JEFFBOG - 01/26/94) // // Focus is being sent to a window which is not a child of the combo // box window which implies that the combo box is losing the focus. // Send a message to the combo box informing him of this fact so // that he can clean up... // SendMessage(ped->hwndParent, CBEC_KILLCOMBOFOCUS, 0, 0L); } } // // If we're still valid, invalidate to cause a redraw. It's common // for some controls be destroyed after losing focus. // if ( IsWindow(hwnd) ) { InvalidateRect(hwnd, NULL, FALSE); } } //---------------------------------------------------------------------------// // // EditSL_Paste() // // Does actual text paste and update. // VOID EditSL_Paste(PED ped) { HDC hdc; // // Insert contents of clipboard, after unhilighting current selection // and deleting it. // Edit_DeleteText(ped); EditSL_PasteText(ped); // // Update display // Edit_NotifyParent(ped, EN_UPDATE); hdc = Edit_GetDC(ped,FALSE); EditSL_ScrollText(ped, hdc); EditSL_DrawText(ped, hdc, 0); Edit_ReleaseDC(ped,hdc,FALSE); // // Tell parent our text contents changed. // Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } //---------------------------------------------------------------------------// // // EditSL_Create // // Creates the edit control for the window hwnd by allocating memory // as required from the application's heap. Notifies parent if no memory // error (after cleaning up if needed). Returns TRUE if no error else return s // -1. // LONG EditSL_Create(PED ped, LPCREATESTRUCT lpCreateStruct) { LPSTR lpWindowText; LONG windowStyle = GET_STYLE(ped); // // Do the standard creation stuff // if (!Edit_Create(ped, windowStyle)) { return -1; } // // Single lines always have no undo and 1 line // ped->cLines = 1; ped->undoType = UNDO_NONE; // // Check if this edit control is part of a combobox and get a pointer to the // combobox structure. // if (windowStyle & ES_COMBOBOX) { ped->listboxHwnd = GetDlgItem(lpCreateStruct->hwndParent, CBLISTBOXID); } // // Set the default font to be the system font. // if ( !Edit_SetFont(ped, NULL, FALSE) ) { // If setting the font fails, our textmetrics can potentially be left // unitialize. Fail to create the control. return -1; } // // Set the window text if needed. Return false if we can't set the text // SLSetText notifies the parent in case there is a no memory error. // lpWindowText = (LPSTR)lpCreateStruct->lpszName; if ((lpWindowText != NULL) && !IsEmptyString(lpWindowText, ped->fAnsi) && !Edit_SetEditText(ped, lpWindowText)) { return -1; } if (windowStyle & ES_PASSWORD) { LOGFONT lfFont = {0}; LoadString(HINST_THISDLL, IDS_PASSWORDCHARFONT, lfFont.lfFaceName, ARRAYSIZE(lfFont.lfFaceName)); lfFont.lfWeight = FW_NORMAL; lfFont.lfCharSet = DEFAULT_CHARSET; ped->hFontPassword = CreateFontIndirect(&lfFont); if (ped->hFontPassword && Edit_SetFont(ped, ped->hFontPassword, FALSE)) { WCHAR szChar[10]; UINT uChar; LoadString(HINST_THISDLL, IDS_PASSWORDCHAR, szChar, ARRAYSIZE(szChar)); uChar = StrToInt(szChar); Edit_SetPasswordCharHandler(ped, uChar); } else { Edit_SetPasswordCharHandler(ped, (UINT)'*'); } } // // Since memory cleared to 0 when allocated, this should still be NULL // ASSERT(ped->pszCueBannerText == NULL); return TRUE; } //---------------------------------------------------------------------------// // // EditSL_Undo AorW // // Handles UNDO for single line edit controls. // BOOL EditSL_Undo(PED ped) { PBYTE hDeletedText = ped->hDeletedText; BOOL fDelete = (BOOL)(ped->undoType & UNDO_DELETE); ICH cchDeleted = ped->cchDeleted; ICH ichDeleted = ped->ichDeleted; BOOL fUpdate = FALSE; if (ped->undoType == UNDO_NONE) { // // No undo... // return FALSE; } ped->hDeletedText = NULL; ped->cchDeleted = 0; ped->ichDeleted = (ICH)-1; ped->undoType &= ~UNDO_DELETE; if (ped->undoType == UNDO_INSERT) { ped->undoType = UNDO_NONE; // // Set the selection to the inserted text // EditSL_SetSelection(ped, ped->ichInsStart, ped->ichInsEnd); ped->ichInsStart = ped->ichInsEnd = (ICH)-1; // // Delete the selected text and save it in undo buff. // Call Edit_DeleteText() instead of sending a VK_BACK message // which results in an EN_UPDATE notification send even before // we insert the deleted chars. This results in Bug #6610. // Fix for Bug #6610 -- SANKAR -- 04/19/91 -- // if (Edit_DeleteText(ped)) { // // Text was deleted -- flag for update and clear selection // fUpdate = TRUE; EditSL_SetSelection(ped, ichDeleted, ichDeleted); } } if (fDelete) { HWND hwndSave = ped->hwnd; // Used for validation. // // Insert deleted chars. Set the selection to the inserted text. // EditSL_SetSelection(ped, ichDeleted, ichDeleted); EditSL_InsertText(ped, hDeletedText, cchDeleted); GlobalFree(hDeletedText); if (!IsWindow(hwndSave)) { return FALSE; } EditSL_SetSelection(ped, ichDeleted, ichDeleted + cchDeleted); fUpdate = TRUE; } if (fUpdate) { // // If we have something to update, send EN_UPDATE before and // EN_CHANGE after the actual update. // A part of the fix for Bug #6610 -- SANKAR -- 04/19/91 -- // Edit_NotifyParent(ped, EN_UPDATE); if (IsWindowVisible(ped->hwnd)) { Edit_InvalidateClient(ped, FALSE); } Edit_NotifyParent(ped, EN_CHANGE); NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, ped->hwnd, OBJID_CLIENT, INDEXID_CONTAINER); } return TRUE; } //---------------------------------------------------------------------------// // // EditSL_SetCueBanner (Unicode Only) // // Handles setting the cue banner text for an edit control. // BOOL EditSL_SetCueBanner(PED ped, LPCWSTR pszBanner) { BOOL retVal = FALSE; if (pszBanner != NULL) { // // Store the input string into the ped's pointer. Str_SetPtr will // allocate/free memory as needed. // retVal = Str_SetPtr(&(ped->pszCueBannerText), pszBanner); // // Redraw the control // InvalidateRect(ped->hwnd, NULL, FALSE); } return retVal; } //---------------------------------------------------------------------------// // // EditSL_WndProc // // Class procedure for all single line edit controls. // Dispatches all messages to the appropriate handlers which are named // as follows: // EditSL_ (single line) prefixes all single line edit control procedures while // Edit_ (edit control) prefixes all common handlers. // // The EditSL_WndProc only handles messages specific to single line edit // controls. // LRESULT EditSL_WndProc(PED ped, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; POINT pt; switch (message) { case WM_INPUTLANGCHANGE: if (ped && ped->fFocus && ped->pLpkEditCallout) { UINT cxCaret; SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0); HideCaret(ped->hwnd); hdc = Edit_GetDC(ped, TRUE); DestroyCaret(); ped->pLpkEditCallout->EditCreateCaret ((PED0)ped, hdc, cxCaret, ped->lineHeight, (UINT)lParam); EditSL_SetCaretPosition(ped, hdc); Edit_ReleaseDC(ped, hdc, TRUE); ShowCaret(ped->hwnd); } goto PassToDefaultWindowProc; case WM_STYLECHANGED: if (ped && ped->pLpkEditCallout) { switch (wParam) { case GWL_STYLE: Edit_UpdateFormat(ped, ((LPSTYLESTRUCT)lParam)->styleNew, GET_EXSTYLE(ped)); return 1L; case GWL_EXSTYLE: Edit_UpdateFormat(ped, GET_STYLE(ped), ((LPSTYLESTRUCT)lParam)->styleNew); return 1L; } } goto PassToDefaultWindowProc; case WM_CHAR: // // wParam - the value of the key // lParam - modifiers, repeat count etc (not used) // if (!ped->fEatNextChar) { EditSL_Char(ped, (UINT)wParam); } else { ped->fEatNextChar = FALSE; } break; case WM_ERASEBKGND: // // wParam - device context handle // lParam - not used // We do nothing on this message and we don't want DefWndProc to do // anything, so return 1 // return 1; case WM_GETDLGCODE: { LONG code = DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS; // // If this is a WM_SYSCHAR message generated by the UNDO keystroke // we want this message so we can EAT IT in "case WM_SYSCHAR:" // if (lParam) { switch (((LPMSG)lParam)->message) { case WM_SYSCHAR: if ((HIWORD(((LPMSG)lParam)->lParam) & SYS_ALTERNATE) && ((WORD)wParam == VK_BACK)) { code |= DLGC_WANTMESSAGE; } break; case WM_KEYDOWN: if (( (((WORD)wParam == VK_RETURN) || ((WORD)wParam == VK_ESCAPE)) && (ped->listboxHwnd) && (GetWindowStyle(ped->hwndParent) & CBS_DROPDOWN) && SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L))) { code |= DLGC_WANTMESSAGE; } break; } } return code; } break; case WM_KEYDOWN: // // wParam - virt keycode of the given key // lParam - modifiers such as repeat count etc. (not used) // EditSL_KeyDown(ped, (UINT)wParam, 0); break; case WM_KILLFOCUS: // // wParam - handle of the window that receives the input focus // lParam - not used // EditSL_KillFocus(ped, (HWND)wParam); break; case WM_CAPTURECHANGED: if (ped->fMouseDown) { ped->fMouseDown = FALSE; } break; case WM_MOUSEMOVE: UserAssert(ped->fMouseDown); // // FALL THRU // case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONUP: // // wParam - contains a value that indicates which virtual keys are down // lParam - contains x and y coords of the mouse cursor // POINTSTOPOINT(pt, lParam); EditSL_MouseMotion(ped, message, (UINT)wParam, &pt); break; case WM_CREATE: // // wParam - handle to window being created // lParam - points to a CREATESTRUCT that contains copies of parameters // passed to the CreateWindow function. // return EditSL_Create(ped, (LPCREATESTRUCT)lParam); break; case WM_PRINTCLIENT: // // wParam -- can be hdc from subclassed paint // lParam -- unused // EditSL_Paint(ped, (HDC) wParam); break; case WM_PAINT: // // wParam -- can be hdc from subclassed paint // lParam -- unused // if (wParam) { hdc = (HDC) wParam; } else { // // this hide/show caret is outside Begin/EndPaint to handle the // case when the caret is half in/half out of the update region // HideCaret(ped->hwnd); hdc = BeginPaint(ped->hwnd, &ps); } // // PORTPORT: Note the symantics of IsWindowVisible and _IsWindowVisible are // slightly different. // if (IsWindowVisible(ped->hwnd)) { EditSL_Paint(ped, hdc); } if (!wParam) { EndPaint(ped->hwnd, &ps); ShowCaret(ped->hwnd); } break; case WM_PASTE: // // wParam - not used // lParam - not used // if (!ped->fReadOnly) { EditSL_Paste(ped); } break; case WM_SETFOCUS: // // wParam - handle of window that loses the input focus (may be NULL) // lParam - not used // EditSL_SetFocus(ped); break; case WM_SIZE: // wParam - defines the type of resizing fullscreen, sizeiconic, // sizenormal etc. // lParam - new width in LOWORD, new height in HIGHWORD of client area Edit_Size(ped, NULL, TRUE); return 0; case WM_SYSKEYDOWN: // // wParam -- virtual key code // lParam -- modifiers // // // Are we in a combobox with the Alt key down? // if (ped->listboxHwnd && (lParam & 0x20000000L)) { // // Handle Combobox support. We want alt up or down arrow to behave // like F4 key which completes the combo box selection // if (lParam & 0x1000000) { // // This is an extended key such as the arrow keys not on the // numeric keypad so just drop the combobox. // if (wParam == VK_DOWN || wParam == VK_UP) { goto DropCombo; } else { goto SkipDropCombo; } } if (!(GetKeyState(VK_NUMLOCK) & 1) && (wParam == VK_DOWN || wParam == VK_UP)) { // // NUMLOCK is up and the keypad up or down arrow hit: // eat character generated by keyboard driver. // ped->fEatNextChar = TRUE; } else { goto SkipDropCombo; } DropCombo: if (SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0) & 0x00000001) { // // Extended ui doesn't honor VK_F4. // if (SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0)) { return SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 0, 0); } else { return SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 1, 0); } } else { return SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0); } } SkipDropCombo: if (wParam == VK_BACK) { SendMessage(ped->hwnd, WM_UNDO, 0, 0L); break; } else { goto PassToDefaultWindowProc; } break; case EM_GETLINE: // // wParam - line number to copy (always the first line for SL) // lParam - buffer to copy text to. FIrst word is max # of bytes to copy // return Edit_GetTextHandler(ped, (*(LPWORD)lParam), (LPSTR)lParam, FALSE); case EM_LINELENGTH: // // wParam - ignored // lParam - ignored // return (LONG)ped->cch; case EM_SETSEL: // // wParam -- start pos // lParam -- end pos // EditSL_SetSelection(ped, (ICH)wParam, (ICH)lParam); break; case EM_REPLACESEL: // // wParam - flag for 4.0+ apps saying whether to clear undo // lParam - points to a null terminated string of replacement text // EditSL_ReplaceSel(ped, (LPSTR)lParam); if (!ped->f40Compat || !wParam) { Edit_EmptyUndo(Pundo(ped)); } break; case EM_GETFIRSTVISIBLELINE: // // wParam - not used // lParam - not used // // effects: Returns the first visible line for single line edit controls. // return ped->ichScreenStart; case EM_POSFROMCHAR: // // wParam -- char index in text // lParam -- not used // This function returns the (x,y) position of the character. // y is always 0 for single. // case EM_CHARFROMPOS: // // wParam -- unused // lParam -- pt in edit client coords // This function returns // LOWORD: the position of the _closest_ char // to the passed in point. // HIWORD: the index of the line (always 0 for single) { LONG xyPos; hdc = Edit_GetDC(ped, TRUE); if (message == EM_POSFROMCHAR) { xyPos = MAKELONG(EditSL_IchToLeftXPos(ped, hdc, (ICH)wParam), 0); } else { POINTSTOPOINT(pt, lParam); xyPos = EditSL_MouseToIch(ped, hdc, &pt); } Edit_ReleaseDC(ped, hdc, TRUE); return (LRESULT)xyPos; } case WM_UNDO: case EM_UNDO: EditSL_Undo(ped); break; case EM_SETCUEBANNER: // // This message passes in a LPCWSTR as the lParam to set the // cue banner text. // // Call function to set the text: return (LRESULT)EditSL_SetCueBanner(ped, (LPCWSTR) lParam); break; default: PassToDefaultWindowProc: return DefWindowProc(ped->hwnd, message, wParam, lParam); break; } return 1L; }