#include "ctlspriv.h"
#pragma hdrstop
#include "usrctl32.h"
#include "edit.h"


//---------------------------------------------------------------------------//
#define WS_EX_EDGEMASK (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE)


#define GetCharABCWidthsAorW    ((ped)->fAnsi ? GetCharABCWidthsA : GetCharABCWidthsW)
#define GetCharWidthAorW        ((ped)->fAnsi ? GetCharWidthA : GetCharWidthW)


//---------------------------------------------------------------------------//
INT Edit_GetStateId(PED ped)
{
    INT iStateId;

    if (ped->fDisabled)
    {
        iStateId = ETS_DISABLED;
    }
    else if (ped->fReadOnly)
    {
        iStateId = ETS_READONLY;
    }
    else if (ped->fFocus)
    {
        iStateId = ETS_FOCUSED;
    }
    else if (ped->fHot)
    {
        iStateId = ETS_HOT;
    }
    else
    {
        iStateId = ETS_NORMAL;
    }

    return iStateId;
}


//---------------------------------------------------------------------------//
VOID Edit_SetMargin(PED ped, UINT  wFlags, long lMarginValues, BOOL fRedraw)
{
    BOOL fUseFontInfo = FALSE;
    UINT wValue, wOldLeftMargin, wOldRightMargin;


    if (wFlags & EC_LEFTMARGIN)
    {
        //
        // Set the left margin
        //
        if ((int) (wValue = (int)(short)LOWORD(lMarginValues)) < 0) 
        {
            fUseFontInfo = TRUE;
            wValue = min((ped->aveCharWidth / 2), (int)ped->wMaxNegA);
        }

        ped->rcFmt.left += wValue - ped->wLeftMargin;
        wOldLeftMargin = ped->wLeftMargin;
        ped->wLeftMargin = wValue;
    }

    if (wFlags & EC_RIGHTMARGIN)
    {
        //
        // Set the Right margin
        //
        if ((int) (wValue = (int)(short)HIWORD(lMarginValues)) < 0) 
        {
            fUseFontInfo = TRUE;
            wValue = min((ped->aveCharWidth / 2), (int)ped->wMaxNegC);
        }

        ped->rcFmt.right -= wValue - ped->wRightMargin;
        wOldRightMargin = ped->wRightMargin;
        ped->wRightMargin = wValue;
    }

    if (fUseFontInfo) 
    {
        if (ped->rcFmt.right - ped->rcFmt.left < 2 * ped->aveCharWidth) 
        {
            TraceMsg(TF_STANDARD, "EDIT: Edit_SetMargin: rcFmt is too narrow for EC_USEFONTINFO");

            if (wFlags & EC_LEFTMARGIN)
            {
                //
                // Reset the left margin
                //
                ped->rcFmt.left += wOldLeftMargin - ped->wLeftMargin;
                ped->wLeftMargin = wOldLeftMargin;
            }

            if (wFlags & EC_RIGHTMARGIN)
            {
                //
                // Reset the Right margin
                //
                ped->rcFmt.right -= wOldRightMargin - ped->wRightMargin;
                ped->wRightMargin = wOldRightMargin;
            }

            return;
        }
    }

    if (fRedraw) 
    {
        Edit_InvalidateClient(ped, TRUE);
    }
}


//---------------------------------------------------------------------------//
VOID Edit_CalcMarginForDBCSFont(PED ped, BOOL fRedraw)
{
    if (ped->fTrueType)
    {
        if (!ped->fSingle) 
        {
            //
            // wMaxNegA came from ABC CharWidth.
            //
            if (ped->wMaxNegA != 0) 
            {
                Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN,
                        MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO),fRedraw);
            }
        } 
        else 
        {
            int    iMaxNegA = 0, iMaxNegC = 0;
            int    i;
            PVOID  lpBuffer;
            LPABC  lpABCBuff;
            ABC    ABCInfo;
            HFONT  hOldFont;
            HDC    hdc = GetDC(ped->hwnd);

            if (!ped->hFont || !(hOldFont = SelectFont(hdc, ped->hFont))) 
            {
                ReleaseDC(ped->hwnd, hdc);
                return;
            }

            if (lpBuffer = UserLocalAlloc(0,sizeof(ABC) * 256)) 
            {
                lpABCBuff = lpBuffer;
                GetCharABCWidthsAorW(hdc, 0, 255, lpABCBuff);
            } 
            else 
            {
                lpABCBuff = &ABCInfo;
                GetCharABCWidthsAorW(hdc, 0, 0, lpABCBuff);
            }

            i = 0;
            while (TRUE) 
            {
                iMaxNegA = min(iMaxNegA, lpABCBuff->abcA);
                iMaxNegC = min(iMaxNegC, lpABCBuff->abcC);

                if (++i == 256)
                {
                    break;
                }

                if (lpBuffer) 
                {
                    lpABCBuff++;
                } 
                else 
                {
                    GetCharABCWidthsAorW(hdc, i, i, lpABCBuff);
                }
            }

            SelectFont(hdc, hOldFont);

            if (lpBuffer)
            {
                UserLocalFree(lpBuffer);
            }

            ReleaseDC(ped->hwnd, hdc);

            if ((iMaxNegA != 0) || (iMaxNegC != 0))
            {
               Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN,
                        MAKELONG((UINT)(-iMaxNegC), (UINT)(-iMaxNegA)),fRedraw);
            }
        }

    }
}


//---------------------------------------------------------------------------//
//
// GetCharDimensionsEx(HDC hDC, HFONT hfont, LPTEXTMETRIC lptm, LPINT lpcy)
//
// if an app set a font for vertical writing, even though we don't
// handle it with EC, the escapement of tm can be NON 0. Then cxWidth from
// GetCharDimenstions() could be 0 in GetCharDimensions().
// This will break our caller who don't expect 0 at return. So I created
// this entry  for the case the caller set vertical font.
//
//
// PORTPORT: Duplicates functionality of GetCharDimensions() in prsht.c
int UserGetCharDimensionsEx(HDC hDC, HFONT hfont, LPTEXTMETRICW lptm, LPINT lpcy)
{
    static CONST WCHAR AveCharWidthData[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int         cxWidth;
    TEXTMETRICW tm;
    LOGFONTW    lf;
    WCHAR       wchFaceName[LF_FACESIZE];

    //
    // Is this font vertical font ??
    //
    wchFaceName[0] = 0;
    GetTextFaceW(hDC, LF_FACESIZE, wchFaceName);
    if (wchFaceName[0] != L'@') 
    {
        //
        // if not call GDI...
        //
        return(GdiGetCharDimensions(hDC, lptm, lpcy));
    }

    if (!lptm)
    {
        lptm = &tm;
    }

    GetTextMetricsW(hDC, lptm);

    // TMPF_FIXED_PITCH
    //
    //   If this bit is set the font is a variable pitch font.
    //   If this bit is clear the font is a fixed pitch font.
    // Note very carefully that those meanings are the opposite of what the constant name implies.
    //
    if (!(lptm->tmPitchAndFamily & TMPF_FIXED_PITCH)) 
    {
        //
        // This is fixed pitch font....
        //
        cxWidth = lptm->tmAveCharWidth;
    } 
    else 
    {
        //
        // This is variable pitch font...
        //
        if (hfont && GetObjectW(hfont, sizeof(LOGFONTW), &lf) && (lf.lfEscapement != 0)) 
        {
            cxWidth = lptm->tmAveCharWidth;
        } 
        else 
        {
            SIZE size;
            GetTextExtentPointW(hDC, AveCharWidthData, 52, &size);
            cxWidth = ((size.cx / 26) + 1) / 2;
        }
    }

    if (lpcy)
    {
        *lpcy = lptm->tmHeight;
    }

    return cxWidth;
}


//---------------------------------------------------------------------------//
//
// Edit_GetTextHandler AorW
//
// Copies at most maxCchToCopy chars to the buffer lpBuffer. Returns
// how many chars were actually copied. Null terminates the string based
// on the fNullTerminate flag:
// fNullTerminate --> at most (maxCchToCopy - 1) characters will be copied
// !fNullTerminate --> at most (maxCchToCopy) characters will be copied
//
ICH Edit_GetTextHandler(PED ped, ICH maxCchToCopy, LPSTR lpBuffer, BOOL fNullTerminate)
{
    PSTR pText;

    if (maxCchToCopy) 
    {
        //
        // Zero terminator takes the extra byte
        //
        if (fNullTerminate)
        {
            maxCchToCopy--;
        }
        maxCchToCopy = min(maxCchToCopy, ped->cch);

        //
        // Zero terminate the string
        //
        if (ped->fAnsi)
        {
            *(LPSTR)(lpBuffer + maxCchToCopy) = 0;
        }
        else
        {
            *(((LPWSTR)lpBuffer) + maxCchToCopy) = 0;
        }

        pText = Edit_Lock(ped);
        RtlCopyMemory(lpBuffer, pText, maxCchToCopy*ped->cbChar);
        Edit_Unlock(ped);
    }

    return maxCchToCopy;
}


//---------------------------------------------------------------------------//
BOOL Edit_NcCreate( PED ped, HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
    BOOL    fAnsi;
    ULONG   ulStyle;
    ULONG   ulStyleEx;

    //
    // Initialize the ped
    //
    ped->hwnd = hwnd;
    ped->pww = (PWW)GetWindowLongPtr(hwnd, GWLP_WOWWORDS);

    ulStyle = GET_STYLE(ped);
    ulStyleEx = GET_EXSTYLE(ped);

    //
    // (phellyar) All strings sent to us via standard WM_* messages or 
    //            control specific EM_* messages are expanded to unicode 
    //            for us by user. Therefore we need worry about 
    //            whether be are created by and ANSI app.
    //
    //fAnsi = TESTFLAG(GET_STATE(ped), WS_ST_ANSICREATOR);
    fAnsi = 0;

    ped->fEncoded = FALSE;
    ped->iLockLevel = 0;

    ped->chLines = NULL;
    ped->pTabStops = NULL;
    ped->charWidthBuffer = NULL;
    ped->fAnsi = fAnsi ? 1 : 0; // Force TRUE to be 1 because its a 1 bit field
    ped->cbChar = (WORD)(fAnsi ? sizeof(CHAR) : sizeof(WCHAR));
    ped->hInstance = lpCreateStruct->hInstance;
    // IME
    ped->hImcPrev = NULL_HIMC;

    {
        DWORD dwVer = UserGetVersion();

        ped->fWin31Compat = Is310Compat(dwVer);
        ped->f40Compat = Is400Compat(dwVer);

    }

    //
    // NOTE:
    // The order of the following two checks is important.  People can
    // create edit fields with a 3D and a normal border, and we don't
    // want to disallow that.  But we need to detect the "no 3D border"
    // border case too.
    //
    if ( ulStyleEx & WS_EX_EDGEMASK )
    {
        ped->fBorder = TRUE;
    }
    else if ( ulStyle & WS_BORDER )
    {
        ClearWindowState(hwnd, WS_BORDER);
        ped->fFlatBorder = TRUE;
        ped->fBorder = TRUE;
    }

    if ( !(ulStyle & ES_MULTILINE) )
    {
        ped->fSingle = TRUE;
    }

    if ( ulStyle & WS_DISABLED )
    {
        ped->fDisabled = TRUE;
    }

    if ( ulStyle & ES_READONLY) 
    {
        if (!ped->fWin31Compat) 
        {
            //
            // BACKWARD COMPATIBILITY HACK
            // 
            // "MileStone" unknowingly sets the ES_READONLY style. So, we strip this
            // style here for all Win3.0 apps (this style is new for Win3.1).
            // Fix for Bug #12982 -- SANKAR -- 01/24/92 --
            //
            ClearWindowState(hwnd, ES_READONLY);
        } 
        else
        {
            ped->fReadOnly = TRUE;
        }
    }


    //
    // Allocate storage for the text for the edit controls. Storage for single
    // line edit controls will always get allocated in the local data segment.
    // Multiline will allocate in the local ds but the app may free this and
    // allocate storage elsewhere...
    //
    ped->hText = LocalAlloc(LHND, CCHALLOCEXTRA*ped->cbChar);
    if (!ped->hText) 
    {
        return FALSE;
    }

    ped->cchAlloc = CCHALLOCEXTRA;
    ped->lineHeight = 1;

    ped->hwndParent = lpCreateStruct->hwndParent;
    ped->hTheme = OpenThemeData(ped->hwnd, L"Edit");

    ped->wImeStatus = 0;

    return (BOOL)DefWindowProc(hwnd, WM_NCCREATE, 0, (LPARAM)lpCreateStruct);
}


//---------------------------------------------------------------------------//
BOOL Edit_Create(PED ped, LONG windowStyle)
{
    HDC hdc;

    //
    // Get values from the window instance data structure and put 
    // them in the ped so that we can access them easier.
    //
    if ( windowStyle & ES_AUTOHSCROLL )
    {
        ped->fAutoHScroll = 1;
    }

    if ( windowStyle & ES_NOHIDESEL )
    {
        ped->fNoHideSel = 1;
    }

    ped->format = (LOWORD(windowStyle) & LOWORD(ES_FMTMASK));
    if ((windowStyle & ES_RIGHT) && !ped->format)
    {
        ped->format = ES_RIGHT;
    }

    //
    // Max # chars we will initially allow
    //
    ped->cchTextMax = MAXTEXT;

    //
    // Set up undo initial conditions... (ie. nothing to undo)
    //
    ped->ichDeleted = (ICH)-1;
    ped->ichInsStart = (ICH)-1;
    ped->ichInsEnd = (ICH)-1;

    //
    // initial charset value - need to do this BEFORE EditML_Create is called
    // so that we know not to fool with scrollbars if nessacary
    //
    hdc = Edit_GetDC(ped, TRUE);
    ped->charSet = (BYTE)GetTextCharset(hdc);
    Edit_ReleaseDC(ped, hdc, TRUE);

    //
    // FE_IME
    // EC_INSERT_COMPOSITION_CHARACTER: Edit_Create() - call Edit_InitInsert()
    //
    Edit_InitInsert(ped, GetKeyboardLayout(0));


    if( g_pLpkEditCallout )
    {
        ped->pLpkEditCallout = g_pLpkEditCallout;
    } 
    else
    {
        ped->pLpkEditCallout = NULL;
    }

    return ped->pLpkEditCallout ? ped->pLpkEditCallout->EditCreate((PED0)ped, ped->hwnd) : TRUE;
}


//---------------------------------------------------------------------------//
//
// Do this once at process startup. The edit control has special
// callouts in lpk.dll to help it render complex script languages
// such as Arabic. The registry probing alg executed here is the same
// as the one performed in win32k!InitializeGre.
//
// We then call GetModuleHandle rather than LoadLibrary, since lpk.dll
// will be guaranteed to be loaded and initialized already by gdi32. This 
// fixes the scenario in which the user turns on complex scripts but
// doesn't reboot, which caused us to try and load lpk without it having
// been initialized on the kernel side.
// 
VOID InitEditLpk()
{
    LONG lError;
    HKEY hKey;

    lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                          TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\LanguagePack"),
                          0,
                          KEY_QUERY_VALUE,
                          &hKey);

    if (lError == ERROR_SUCCESS)
    {
        HANDLE hLpk;
        DWORD  dwLpkShapingDlls;
        DWORD  dwIndex;
        TCHAR  szTemp[256];
        DWORD  dwTempSize;
        DWORD  dwValueType;
        DWORD  dwValue;
        DWORD  dwValueSize;

        dwLpkShapingDlls = 0;
        dwIndex = 0;
        do 
        {
            dwTempSize  = ARRAYSIZE(szTemp);
            dwValueSize = SIZEOF(DWORD);
            lError = RegEnumValue(hKey,
                                  dwIndex++,
                                  szTemp,
                                  &dwTempSize,
                                  NULL,
                                  &dwValueType,
                                  (LPVOID)&dwValue,
                                  &dwValueSize);

            if ((lError == ERROR_SUCCESS) && (dwValueType == REG_DWORD))
            {
                dwLpkShapingDlls |= 1 << dwValue;
            }
        } 
        while (lError != ERROR_NO_MORE_ITEMS);

        if (dwLpkShapingDlls != 0)
        {
            hLpk = GetModuleHandle(TEXT("LPK"));
            if (hLpk != NULL)
            {
                g_pLpkEditCallout = (PLPKEDITCALLOUT)GetProcAddress(hLpk, "LpkEditControl");

                if (g_pLpkEditCallout == NULL)
                {
                    FreeLibrary(hLpk);
                }
            }
        }

        RegCloseKey(hKey);
    }
}


//---------------------------------------------------------------------------//
//
// Destroys the edit control ped by freeing up all memory used by it.
//
VOID Edit_NcDestroyHandler(HWND hwnd, PED ped)
{
    //
    // ped could be NULL if WM_NCCREATE failed to create it...
    //
    if (ped) 
    {
        //
        // Free the text buffer (always present?)
        //
        LocalFree(ped->hText);

        
        //
        // Free up undo buffer and line start array (if present)
        //
        if (ped->hDeletedText != NULL)
        {
            GlobalFree(ped->hDeletedText);
        }

        //
        // Free tab stop buffer (if present)
        //
        if (ped->pTabStops)
        {
            UserLocalFree(ped->pTabStops);
        }

        //
        // Free line start array (if present)
        //
        if (ped->chLines) 
        {
            UserLocalFree(ped->chLines);
        }

        //
        // Free the character width buffer (if present)
        //
        if (ped->charWidthBuffer)
        {
            UserLocalFree(ped->charWidthBuffer);
        }

        //
        // Free the cursor bitmap
        //
        if (ped->pLpkEditCallout && ped->hCaretBitmap)
        {
            DeleteObject(ped->hCaretBitmap);
        }

        //
        // Free the cached font handle
        //
        if ( ped->hFontSave )
        {
            DeleteObject(ped->hFontSave);
        }

        //
        // Close an open theme handle
        //
        if ( ped->hTheme )
        {
            CloseThemeData(ped->hTheme);
        }

        // 
        // Free the memory used by CueBannerText
        //
        Str_SetPtr(&(ped->pszCueBannerText), NULL);

        //
        // free the allocated password font
        //
        if ( ped->hFontPassword )
        {
            DeleteObject(ped->hFontPassword);
        }

        //
        // Last but not least, free the ped
        //
        UserLocalFree(ped);
    }

    TraceMsg(TF_STANDARD, "EDIT: Clearing edit instance pointer.");
    Edit_SetPtr(hwnd, NULL);

    // If we're part of a combo box, let it know we're gone
#if 0 // PORTPORT: Expose fnid field in WND struct.
    pwndParent = REBASEPWND(pwnd, spwndParent);
    if (pwndParent && GETFNID(pwndParent) == FNID_COMBOBOX) {
        ComboBoxWndProcWorker(pwndParent, WM_PARENTNOTIFY,
                MAKELONG(WM_DESTROY, PTR_TO_ID(pwnd->spmenu)), (LPARAM)HWq(pwnd), FALSE);
    }
#endif
}


//---------------------------------------------------------------------------//
// 
// Edit_SetPasswordCharHandler AorW
//
// Sets the password char to display.
//
VOID Edit_SetPasswordCharHandler(PED ped, UINT pwchar)
{
    HDC hdc;
    SIZE size = {0};

    ped->charPasswordChar = pwchar;

    if (pwchar) 
    {
        hdc = Edit_GetDC(ped, TRUE);

        if (ped->fAnsi)
        {
            GetTextExtentPointA(hdc, (LPSTR)&pwchar, 1, &size);
        }
        else
        {
            GetTextExtentPointW(hdc, (LPWSTR)&pwchar, 1, &size);
        }

        GetTextExtentPointW(hdc, (LPWSTR)&pwchar, 1, &size);
        ped->cPasswordCharWidth = max(size.cx, 1);
        Edit_ReleaseDC(ped, hdc, TRUE);
    }

    if (pwchar)
    {
        SetWindowState(ped->hwnd, ES_PASSWORD);
    }
    else
    {
        ClearWindowState(ped->hwnd, ES_PASSWORD);
    }

    if ( g_fIMMEnabled )
    {
        Edit_EnableDisableIME(ped);
    }
}


//---------------------------------------------------------------------------//
//
// GetNegABCwidthInfo()
//
// This function fills up the ped->charWidthBuffer buffer with the
// negative A,B and C widths for all the characters below 0x7f in the
// currently selected font.
//
// Returns:
//   TRUE, if the function succeeded.
//   FALSE, if GDI calls to get the char widths have failed.
//
// Note: not used if LPK installed
// 
BOOL GetNegABCwidthInfo(PED ped, HDC hdc)
{
    LPABC lpABCbuff;
    int   i;
    int   CharWidthBuff[CHAR_WIDTH_BUFFER_LENGTH]; // Local char width buffer.
    int   iOverhang;

    if (!GetCharABCWidthsA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, (LPABC)ped->charWidthBuffer)) 
    {
        TraceMsg(TF_STANDARD, "UxEdit: GetNegABCwidthInfo: GetCharABCWidthsA Failed");
        return FALSE;
    }

    // 
    // The (A+B+C) returned for some fonts (eg: Lucida Caligraphy) does not
    // equal the actual advanced width returned by GetCharWidths() minus overhang.
    // This is due to font bugs. So, we adjust the 'B' width so that this
    // discrepancy is removed.
    // Fix for Bug #2932 --sankar-- 02/17/93
    //
    iOverhang = ped->charOverhang;
    GetCharWidthA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, (LPINT)CharWidthBuff);
    lpABCbuff = (LPABC)ped->charWidthBuffer;
    for(i = 0; i < CHAR_WIDTH_BUFFER_LENGTH; i++) 
    {
         lpABCbuff->abcB = CharWidthBuff[i] - iOverhang
                 - lpABCbuff->abcA
                 - lpABCbuff->abcC;
         lpABCbuff++;
    }

    return TRUE;
}


//---------------------------------------------------------------------------//
//
// Edit_Size() -
//
// Handle sizing for an edit control's client rectangle.
// Use lprc as the bounding rectangle if specified; otherwise use the current
// client rectangle.
//
VOID Edit_Size(PED ped, LPRECT lprc, BOOL fRedraw)
{
    RECT rc;

    //
    // BiDi VB32 Creates an Edit Control and immediately sends a WM_SIZE
    // message which causes EXSize to be called before Edit_SetFont, which
    // in turn causes a divide by zero exception below. This check for
    // ped->lineHeight will pick it up safely. [samera] 3/5/97
    //
    if(ped->lineHeight == 0)
    {
        return;
    }

    //
    // assume that we won't be able to display the caret
    //
    ped->fCaretHidden = TRUE;


    if ( lprc )
    {
        CopyRect(&rc, lprc);
    }
    else
    {
        GetClientRect(ped->hwnd, &rc);
    }

    if (!(rc.right - rc.left) || !(rc.bottom - rc.top)) 
    {
        if (ped->rcFmt.right - ped->rcFmt.left)
        {
            return;
        }

        rc.left     = 0;
        rc.top      = 0;
        rc.right    = ped->aveCharWidth * 10;
        rc.bottom   = ped->lineHeight;
    }

    if (!lprc) 
    {
        //
        // subtract the margins from the given rectangle --
        // make sure that this rectangle is big enough to have these margins.
        //
        if ((rc.right - rc.left) > (int)(ped->wLeftMargin + ped->wRightMargin)) 
        {
            rc.left  += ped->wLeftMargin;
            rc.right -= ped->wRightMargin;
        }
    }

    //
    // Leave space so text doesn't touch borders.
    // For 3.1 compatibility, don't subtract out vertical borders unless
    // there is room.
    //
    if (ped->fBorder) 
    {
        INT cxBorder = GetSystemMetrics(SM_CXBORDER);
        INT cyBorder = GetSystemMetrics(SM_CYBORDER);

        if (ped->fFlatBorder)
        {
            cxBorder *= 2;
            cyBorder *= 2;
        }

        if (rc.bottom < rc.top + ped->lineHeight + 2*cyBorder)
        {
            cyBorder = 0;
        }

        InflateRect(&rc, -cxBorder, -cyBorder);
    }

    //
    // Is the resulting rectangle too small?  Don't change it then.
    //
    if ((!ped->fSingle) && ((rc.right - rc.left < (int) ped->aveCharWidth) ||
        ((rc.bottom - rc.top) / ped->lineHeight == 0)))
    {
        return;
    }

    //
    // now, we know we're safe to display the caret
    //
    ped->fCaretHidden = FALSE;

    CopyRect(&ped->rcFmt, &rc);

    if (ped->fSingle)
    {
        ped->rcFmt.bottom = min(rc.bottom, rc.top + ped->lineHeight);
    }
    else
    {
        EditML_Size(ped, fRedraw);
    }

    if (fRedraw) 
    {
        InvalidateRect(ped->hwnd, NULL, TRUE);
    }

    //
    // FE_IME
    // Edit_Size()  - call Edit_ImmSetCompositionWindow()
    //
    // normally this isn't needed because WM_SIZE will cause
    // WM_PAINT and the paint handler will take care of IME
    // composition window. However when the edit window is
    // restored from maximized window and client area is out
    // of screen, the window will not be redrawn.
    //
    if (ped->fFocus && g_fIMMEnabled && ImmIsIME(GetKeyboardLayout(0))) 
    {
        POINT pt;

        GetCaretPos(&pt);
        Edit_ImmSetCompositionWindow(ped, pt.x, pt.y);
    }
}


//---------------------------------------------------------------------------//
// 
// Edit_SetFont AorW
// 
// Sets the font used in the edit control.  Warning:  Memory compaction may
// occur if the font wasn't previously loaded.  If the font handle passed
// in is NULL, assume the system font.
//
BOOL Edit_SetFont(PED ped, HFONT hfont, BOOL fRedraw)
{
    TEXTMETRICW TextMetrics = {0};
    HDC         hdc;
    HFONT       hOldFont=NULL;
    DWORD       dwMaxOverlapChars;
    CHWIDTHINFO cwi;
    UINT        uExtracharPos;
    BOOL        fRet = FALSE;

    hdc = GetDC(ped->hwnd);
    if (hdc)
    {

#ifdef _USE_DRAW_THEME_TEXT_
        if (hfont)
        {
            ped->hFontSave = hfont;
        }

        if ( ped->hTheme )
        {
            //
            // use the theme font if we're themed
            //
            HRESULT hr;
            LOGFONT lf;
            hr = GetThemeFont(ped->hTheme, hdc, EP_EDITTEXT, 0, TMT_FONT, &lf);
            if ( SUCCEEDED(hr) )
            {
                hfont = CreateFontIndirect(&lf);
            }
        }
#endif // _USE_DRAW_THEME_TEXT_

        ped->hFont = hfont;
        if (ped->hFont)
        {
            //
            // Since the default font is the system font, no need to select it in
            // if that's what the user wants.
            //
            hOldFont = SelectObject(hdc, hfont);
            if (!hOldFont) 
            {
                hfont = ped->hFont = NULL;
            }

            //
            // Get the metrics and ave char width for the currently selected font
            //

            //
            // Call Vertical font-aware AveWidth compute function...
            //
            // FE_SB
            ped->aveCharWidth = UserGetCharDimensionsEx(hdc, hfont, &TextMetrics, &ped->lineHeight);

            //
            // This might fail when people uses network fonts (or bad fonts).
            //
            if (ped->aveCharWidth == 0) 
            {
                TraceMsg(TF_STANDARD, "EDIT: Edit_SetFont: GdiGetCharDimensions failed");
                if (hOldFont != NULL) 
                {
                    SelectObject(hdc, hOldFont);
                }

                //
                // We've messed up the ped so let's reset the font.
                // Note that we won't recurse more than once because we'll
                // pass hfont == NULL.
                // Too bad WM_SETFONT doesn't return a value.
                //
                return Edit_SetFont(ped, NULL, fRedraw);
            }
        } 
        else 
        {
            ped->aveCharWidth = UserGetCharDimensionsEx(hdc, hfont, &TextMetrics, &ped->lineHeight);

            // We should always be able to get the dimensions of the system font. Just in case
            // set these guys to known system font constants
            if ( ped->aveCharWidth == 0 )
            {
                ped->aveCharWidth = SYSFONT_CXCHAR;
                ped->lineHeight = SYSFONT_CYCHAR;
            }
        }

        ped->charOverhang = TextMetrics.tmOverhang;

        //
        // assume that they don't have any negative widths at all.
        //
        ped->wMaxNegA = ped->wMaxNegC = ped->wMaxNegAcharPos = ped->wMaxNegCcharPos = 0;

        //
        // Check if Proportional Width Font
        //
        // NOTE: as SDK doc says about TEXTMETRIC:
        // TMPF_FIXED_PITCH
        // If this bit is set the font is a variable pitch font. If this bit is clear
        // the font is a fixed pitch font. Note very carefully that those meanings are
        // the opposite of what the constant name implies.
        //
        // Thus we have to reverse the value using logical not (fNonPropFont has 1 bit width)
        //
        ped->fNonPropFont = !(TextMetrics.tmPitchAndFamily & FIXED_PITCH);

        //
        // Check for a TrueType font
        // Older app OZWIN chokes if we allocate a bigger buffer for TrueType fonts
        // So, for apps older than 4.0, no special treatment for TrueType fonts.
        //
        if (ped->f40Compat && (TextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) 
        {
            ped->fTrueType = GetCharWidthInfo(hdc, &cwi);
#if DBG
            if (!ped->fTrueType) 
            {
                TraceMsg(TF_STANDARD, "EDIT: Edit_SetFont: GetCharWidthInfo Failed");
            }
#endif
        } 
        else 
        {
            ped->fTrueType = FALSE;
        }

        // FE_SB
        //
        // In DBCS Windows, Edit Control must handle Double Byte Character
        // if tmCharSet field of textmetrics is double byte character set
        // such as SHIFTJIS_CHARSET(128:Japan), HANGEUL_CHARSET(129:Korea).
        //
        // We call Edit_GetDBCSVector even when fAnsi is false so that we could
        // treat ped->fAnsi and ped->fDBCS indivisually. I changed Edit_GetDBCSVector
        // function so that it returns 0 or 1, because I would like to set ped->fDBCS
        // bit field here.
        //
        ped->fDBCS = Edit_GetDBCSVector(ped,hdc,TextMetrics.tmCharSet);
        ped->charSet = TextMetrics.tmCharSet;

        if (ped->fDBCS) 
        {
            //
            // Free the character width buffer if ped->fDBCS.
            //
            // I expect single GetTextExtentPoint call is faster than multiple
            // GetTextExtentPoint call (because the graphic engine has a cache buffer).
            // See editec.c/ECTabTheTextOut().
            //
            if (ped->charWidthBuffer) 
            {
                LocalFree(ped->charWidthBuffer);
                ped->charWidthBuffer = NULL;
            }

            //
            // if FullWidthChar : HalfWidthChar == 2 : 1....
            //
            // TextMetrics.tmMaxCharWidth = FullWidthChar width
            // ped->aveCharWidth          = HalfWidthChar width
            //
            if (ped->fNonPropFont &&
                ((ped->aveCharWidth * 2) == TextMetrics.tmMaxCharWidth)) 
            {
                ped->fNonPropDBCS = TRUE;
            } 
            else 
            {
                ped->fNonPropDBCS = FALSE;
            }

        } 
        else 
        {
            //
            // Since the font has changed, let us obtain and save the character width
            // info for this font.
            //
            // First left us find out if the maximum chars that can overlap due to
            // negative widths. Since we can't access USER globals, we make a call here.
            //
            if (!(ped->fSingle || ped->pLpkEditCallout)) 
            {
                //
                // Is this a multiline edit control with no LPK present?
                //
                UINT  wBuffSize;
                LPINT lpCharWidthBuff;
                SHORT i;

                //
                // For multiline edit controls, we maintain a buffer that contains
                // the character width information.
                //
                wBuffSize = (ped->fTrueType) ? (CHAR_WIDTH_BUFFER_LENGTH * sizeof(ABC)) :
                                               (CHAR_WIDTH_BUFFER_LENGTH * sizeof(int));

                if (ped->charWidthBuffer) 
                {
                    //
                    // If buffer already present
                    //
                    lpCharWidthBuff = ped->charWidthBuffer;
                    ped->charWidthBuffer = UserLocalReAlloc(lpCharWidthBuff, wBuffSize, HEAP_ZERO_MEMORY);
                    if (ped->charWidthBuffer == NULL) 
                    {
                        UserLocalFree((HANDLE)lpCharWidthBuff);
                    }
                } 
                else 
                {
                    ped->charWidthBuffer = UserLocalAlloc(HEAP_ZERO_MEMORY, wBuffSize);
                }

                if (ped->charWidthBuffer != NULL) 
                {
                    if (ped->fTrueType) 
                    {
                        ped->fTrueType = GetNegABCwidthInfo(ped, hdc);
                    }

                    //
                    // It is possible that the above attempts could have failed and reset
                    // the value of fTrueType. So, let us check that value again.
                    //
                    if (!ped->fTrueType) 
                    {
                        if (!GetCharWidthA(hdc, 0, CHAR_WIDTH_BUFFER_LENGTH-1, ped->charWidthBuffer)) 
                        {
                            UserLocalFree((HANDLE)ped->charWidthBuffer);
                            ped->charWidthBuffer=NULL;
                        } 
                        else 
                        {
                            //
                            // We need to subtract out the overhang associated with
                            // each character since GetCharWidth includes it...
                            //
                            for (i=0;i < CHAR_WIDTH_BUFFER_LENGTH;i++)
                            {
                                ped->charWidthBuffer[i] -= ped->charOverhang;
                            }
                        }
                    }
                }
            }
        }

        {
            //
            // Calculate MaxNeg A C metrics
            //
#if 0 // PORTPORT: How to get this info in user mode? And if we
          //           set it to zero what is the effect?
            dwMaxOverlapChars = GetMaxOverlapChars();
#endif
            dwMaxOverlapChars = 0; 
            if (ped->fTrueType) 
            {
                if (cwi.lMaxNegA < 0)
                {
                    ped->wMaxNegA = -cwi.lMaxNegA;
                }
                else
                {
                    ped->wMaxNegA = 0;
                }

                if (cwi.lMaxNegC < 0)
                {
                    ped->wMaxNegC = -cwi.lMaxNegC;
                }
                else
                {
                    ped->wMaxNegC = 0;
                }

                if (cwi.lMinWidthD != 0) 
                {
                    ped->wMaxNegAcharPos = (ped->wMaxNegA + cwi.lMinWidthD - 1) / cwi.lMinWidthD;
                    ped->wMaxNegCcharPos = (ped->wMaxNegC + cwi.lMinWidthD - 1) / cwi.lMinWidthD;
                    if (ped->wMaxNegA + ped->wMaxNegC > (UINT)cwi.lMinWidthD) 
                    {
                        uExtracharPos = (ped->wMaxNegA + ped->wMaxNegC - 1) / cwi.lMinWidthD;
                        ped->wMaxNegAcharPos += uExtracharPos;
                        ped->wMaxNegCcharPos += uExtracharPos;
                    }
                } 
                else 
                {
                    ped->wMaxNegAcharPos = LOWORD(dwMaxOverlapChars);     // Left
                    ped->wMaxNegCcharPos = HIWORD(dwMaxOverlapChars);     // Right
                }

            } 
            else if (ped->charOverhang != 0) 
            {
                //
                // Some bitmaps fonts (i.e., italic) have under/overhangs;
                // this is pretty much like having negative A and C widths.
                //
                ped->wMaxNegA = ped->wMaxNegC = ped->charOverhang;
                ped->wMaxNegAcharPos = LOWORD(dwMaxOverlapChars);     // Left
                ped->wMaxNegCcharPos = HIWORD(dwMaxOverlapChars);     // Right
            }
        }

        if (!hfont) 
        {
            //
            // We are getting the stats for the system font so update the system
            // font fields in the ed structure since we use these when calculating
            // some spacing.
            //
            ped->cxSysCharWidth = ped->aveCharWidth;
            ped->cySysCharHeight= ped->lineHeight;
        } 
        else if (hOldFont)
        {
            SelectObject(hdc, hOldFont);
        }

        if (ped->fFocus) 
        {
            UINT cxCaret;

            SystemParametersInfo(SPI_GETCARETWIDTH, 0, (LPVOID)&cxCaret, 0);

            //
            // Update the caret.
            //
            HideCaret(ped->hwnd);
            DestroyCaret();

            if (ped->pLpkEditCallout) 
            {
                ped->pLpkEditCallout->EditCreateCaret((PED0)ped, hdc, cxCaret, ped->lineHeight, 0);
            }
            else 
            {
                CreateCaret(ped->hwnd, (HBITMAP)NULL, cxCaret, ped->lineHeight);
            }
            ShowCaret(ped->hwnd);
        }

        ReleaseDC(ped->hwnd, hdc);

        //
        // Update password character.
        //
        if (ped->charPasswordChar)
        {
            Edit_SetPasswordCharHandler(ped, ped->charPasswordChar);
        }

        //
        // If it is a TrueType font and it's a new app, set both the margins at the
        // max negative width values for all types of the edit controls.
        // (NOTE: Can't use ped->f40Compat here because edit-controls inside dialog
        // boxes without DS_LOCALEDIT style are always marked as 4.0 compat.
        // This is the fix for NETBENCH 3.0)
        //

        if (ped->fTrueType && ped->f40Compat)
        {
            if (ped->fDBCS) 
            {
                //
                // For DBCS TrueType Font, we calc margin from ABC width.
                //
                Edit_CalcMarginForDBCSFont(ped, fRedraw);
            } 
            else 
            {
                Edit_SetMargin(ped, EC_LEFTMARGIN | EC_RIGHTMARGIN,
                            MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO), fRedraw);
            }
        }

        //
        // We need to calc maxPixelWidth when font changes.
        // If the word-wrap is ON, then this is done in EditML_Size() called later.
        //
        if((!ped->fSingle) && (!ped->fWrap))
        {
            EditML_BuildchLines(ped, 0, 0, FALSE, NULL, NULL);
        }

        //
        // Recalc the layout.
        //
        Edit_Size(ped, NULL, fRedraw);

        if ( ped->fFocus && ImmIsIME(GetKeyboardLayout(0)) ) 
        {
            Edit_SetCompositionFont( ped );
        }

        fRet = TRUE;
    }

    return fRet;
}


//---------------------------------------------------------------------------//
//
// Edit_IsCharNumeric AorW
//
// Tests whether the character entered is a numeral.
// For multiline and singleline edit controls with the ES_NUMBER style.
// 
BOOL Edit_IsCharNumeric(PED ped, DWORD keyPress)
{
    WORD wCharType;

    if (ped->fAnsi) 
    {
        char ch = (char)keyPress;
        LCID lcid = (LCID)((ULONG_PTR)GetKeyboardLayout(0) & 0xFFFF);
        GetStringTypeA(lcid, CT_CTYPE1, &ch, 1, &wCharType);
    } 
    else 
    {
        WCHAR wch = (WCHAR)keyPress;
        GetStringTypeW(CT_CTYPE1, &wch, 1, &wCharType);
    }
    return (wCharType & C1_DIGIT ? TRUE : FALSE);
}


//---------------------------------------------------------------------------//
VOID Edit_EnableDisableIME(PED ped)
{
    if ( ped->fReadOnly || ped->charPasswordChar ) 
    {
        //
        // IME should be disabled
        //
        HIMC hImc;
        hImc = ImmGetContext( ped->hwnd );

        if ( hImc != NULL_HIMC ) 
        {
            ImmReleaseContext( ped->hwnd, hImc );
            ped->hImcPrev = ImmAssociateContext( ped->hwnd, NULL_HIMC );
        }

    } 
    else 
    {
        //
        // IME should be enabled
        //
        if ( ped->hImcPrev != NULL_HIMC ) 
        {
            ped->hImcPrev = ImmAssociateContext( ped->hwnd, ped->hImcPrev );

            //
            // Font and the caret position might be changed while
            // IME was being disabled. Set those now if the window
            // has the focus.
            //
            if ( ped->fFocus ) 
            {
                POINT pt;

                Edit_SetCompositionFont( ped );

                GetCaretPos( &pt );
                Edit_ImmSetCompositionWindow( ped, pt.x, pt.y  );
            }
        }
    }

    Edit_InitInsert(ped, GetKeyboardLayout(0));
}


//---------------------------------------------------------------------------//
VOID Edit_ImmSetCompositionWindow(PED ped, LONG x, LONG y)
{
    COMPOSITIONFORM cf  = {0};
    COMPOSITIONFORM cft = {0};
    RECT rcScreenWindow;
    HIMC hImc;

    hImc = ImmGetContext(ped->hwnd);
    if ( hImc != NULL_HIMC ) 
    {
        if ( ped->fFocus ) 
        {
            GetWindowRect( ped->hwnd, &rcScreenWindow);

            //
            // assuming RECT.left is the first and and RECT.top is the second field
            //
            MapWindowPoints( ped->hwnd, HWND_DESKTOP, (LPPOINT)&rcScreenWindow, 2);
            if (ped->fInReconversion) 
            {
                DWORD dwPoint = (DWORD)(ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_POSFROMCHAR, ped->ichMinSel, 0);

                x = GET_X_LPARAM(dwPoint);
                y = GET_Y_LPARAM(dwPoint);

                TraceMsg(TF_STANDARD, "UxEdit: Edit_ImmSetCompositionWindow: fInReconversion (%d,%d)", x, y);
            }

            //
            // The window currently has the focus.
            //
            if (ped->fSingle) 
            {
                //
                // Single line edit control.
                //
                cf.dwStyle = CFS_POINT;
                cf.ptCurrentPos.x = x;
                cf.ptCurrentPos.y = y;
                SetRectEmpty(&cf.rcArea);

            } 
            else 
            {
                //
                // Multi line edit control.
                //
                cf.dwStyle = CFS_RECT;
                cf.ptCurrentPos.x = x;
                cf.ptCurrentPos.y = y;
                cf.rcArea = ped->rcFmt;
            }
            ImmGetCompositionWindow( hImc, &cft );
            if ( (!RtlEqualMemory(&cf,&cft,sizeof(COMPOSITIONFORM))) ||
                 (ped->ptScreenBounding.x != rcScreenWindow.left)    ||
                 (ped->ptScreenBounding.y  != rcScreenWindow.top) ) 
            {

                ped->ptScreenBounding.x = rcScreenWindow.left;
                ped->ptScreenBounding.y = rcScreenWindow.top;
                ImmSetCompositionWindow( hImc, &cf );
            }
        }
        ImmReleaseContext( ped->hwnd, hImc );
    }
}


//---------------------------------------------------------------------------//
VOID Edit_SetCompositionFont(PED ped)
{
    HIMC hImc;
    LOGFONTW lf;

    hImc = ImmGetContext( ped->hwnd );
    if (hImc != NULL_HIMC) 
    {
        if (ped->hFont) 
        {
            GetObjectW(ped->hFont, sizeof(LOGFONTW), (LPLOGFONTW)&lf);
        } 
        else 
        {
            GetObjectW(GetStockObject(SYSTEM_FONT), sizeof(LOGFONTW), (LPLOGFONTW)&lf);
        }

        ImmSetCompositionFontW( hImc, &lf );
        ImmReleaseContext( ped->hwnd, hImc );
    }
}


//---------------------------------------------------------------------------//
//
// Edit_InitInsert
//
// this function is called when:
// 1) a edit control window is initialized
// 2) active keyboard layout of current thread is changed
// 3) read only attribute of this edit control is changed
//
VOID Edit_InitInsert( PED ped, HKL hkl )
{
    ped->fKorea = FALSE;
    ped->fInsertCompChr = FALSE;
    ped->fNoMoveCaret = FALSE;
    ped->fResultProcess = FALSE;

    if (g_fIMMEnabled && ImmIsIME(hkl) ) 
    {
        if (PRIMARYLANGID(LOWORD(HandleToUlong(hkl))) == LANG_KOREAN ) 
        {
            ped->fKorea = TRUE;
        }

        //
        // LATER:this flag should be set based on the IME caps
        // retrieved from IME. (Such IME caps should be defined)
        // For now, we can safely assume that only Korean IMEs
        // set CS_INSERTCHAR.
        //
        if ( ped->fKorea ) 
        {
            ped->fInsertCompChr = TRUE;
        }
    }

    //
    // if we had a composition character, the shape of caret
    // is changed. We need to reset the caret shape.
    //
    if ( ped->fReplaceCompChr ) 
    {
        ped->fReplaceCompChr = FALSE;
        Edit_SetCaretHandler( ped );
    }
}


//---------------------------------------------------------------------------//
VOID Edit_SetCaretHandler(PED ped)
{
    HDC     hdc;
    PSTR    pText;
    SIZE    size = {0};

    //
    // In any case destroy caret beforehand otherwise SetCaretPos()
    // will get crazy.. win95d-B#992,B#2370
    //
    if (ped->fFocus) 
    {
        HideCaret(ped->hwnd);
        DestroyCaret();
        if ( ped->fReplaceCompChr ) 
        {

            hdc = Edit_GetDC(ped, TRUE );
            pText = Edit_Lock(ped);

            if ( ped->fAnsi)
            {
                 GetTextExtentPointA(hdc, pText + ped->ichCaret, 2, &size);
            }
            else
            {
                 GetTextExtentPointW(hdc, (LPWSTR)pText + ped->ichCaret, 1, &size);
            }

            Edit_Unlock(ped);
            Edit_ReleaseDC(ped, hdc, TRUE);

            CreateCaret(ped->hwnd, (HBITMAP)NULL, size.cx, ped->lineHeight);
        }
        else 
        {
            CreateCaret(ped->hwnd,
                        (HBITMAP)NULL,
                        (ped->cxSysCharWidth > ped->aveCharWidth ? 1 : 2),
                        ped->lineHeight);
        }

        hdc = Edit_GetDC(ped, TRUE );
        if ( ped->fSingle )
        {
            EditSL_SetCaretPosition( ped, hdc );
        }
        else
        {
            EditML_SetCaretPosition( ped, hdc );
        }

        Edit_ReleaseDC(ped, hdc, TRUE);
        ShowCaret(ped->hwnd);
    }
}


//---------------------------------------------------------------------------//
#define GET_COMPOSITION_STRING  (ped->fAnsi ? ImmGetCompositionStringA : ImmGetCompositionStringW)

BOOL Edit_ResultStrHandler(PED ped)
{
    HIMC himc;
    LPSTR lpStr;
    LONG dwLen;

    ped->fInsertCompChr = FALSE;    // clear the state
    ped->fNoMoveCaret = FALSE;

    himc = ImmGetContext(ped->hwnd);
    if ( himc == NULL_HIMC ) 
    {
        return FALSE;
    }

    dwLen = GET_COMPOSITION_STRING(himc, GCS_RESULTSTR, NULL, 0);

    if (dwLen == 0) 
    {
        ImmReleaseContext(ped->hwnd, himc);
        return FALSE;
    }

    dwLen *= ped->cbChar;
    dwLen += ped->cbChar;

    lpStr = (LPSTR)GlobalAlloc(GPTR, dwLen);
    if (lpStr == NULL) 
    {
        ImmReleaseContext(ped->hwnd, himc);
        return FALSE;
    }

    GET_COMPOSITION_STRING(himc, GCS_RESULTSTR, lpStr, dwLen);

    if (ped->fSingle) 
    {
        EditSL_ReplaceSel(ped, lpStr);
    } 
    else 
    {
        EditML_ReplaceSel(ped, lpStr);
    }

    GlobalFree((HGLOBAL)lpStr);

    ImmReleaseContext(ped->hwnd, himc);

    ped->fReplaceCompChr = FALSE;
    ped->fNoMoveCaret = FALSE;
    ped->fResultProcess = FALSE;

    Edit_SetCaretHandler(ped);

    return TRUE;
}


//---------------------------------------------------------------------------//
LRESULT Edit_ImeComposition(PED ped, WPARAM wParam, LPARAM lParam)
{
    INT ich;
    LRESULT lReturn = 1;
    HDC hdc;
    BOOL fSLTextUpdated = FALSE;
    ICH iResult;
    HIMC hImc;
    BYTE TextBuf[4];

    if (!ped->fInsertCompChr) 
    {
        if (lParam & GCS_RESULTSTR) 
        {
            Edit_InOutReconversionMode(ped, FALSE);

            if (!ped->fKorea && ped->wImeStatus & EIMES_GETCOMPSTRATONCE) 
            {
ResultAtOnce:
                Edit_ResultStrHandler(ped);
                lParam &= ~GCS_RESULTSTR;
            }
        }
        return DefWindowProc(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam);
    }

    //
    // In case of Ansi edit control, the length of minimum composition string
    // is 2. Check here maximum byte of edit control.
    //
    if( ped->fAnsi && ped->cchTextMax == 1 ) 
    {
        HIMC hImc;

        hImc = ImmGetContext( ped->hwnd );
        ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0L);
        ImmReleaseContext( ped->hwnd, hImc );
        MessageBeep(MB_ICONEXCLAMATION);
        return lReturn;
    }

    //
    // Don't move this after CS_NOMOVECARET check.
    // In case if skip the message, fNoMoveCaret should not be set.
    //
    if ((lParam & CS_INSERTCHAR) && ped->fResultProcess) 
    {
        //
        // Now we're in result processing. GCS_RESULTSTR ends up
        // to WM_IME_CHAR and WM_CHAR. Since WM_CHAR is posted,
        // the message(s) will come later than this CS_INSERTCHAR
        // message. This composition character should be handled
        // after the WM_CHAR message(s).
        //
        if(ped->fAnsi)
        {
            PostMessageA(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam);
        }
        else
        {
            PostMessageW(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam);
        }

        ped->fResultProcess = FALSE;

        return lReturn;
    }

    if (lParam & GCS_RESULTSTR) 
    {
        if (!ped->fKorea && ped->wImeStatus & EIMES_GETCOMPSTRATONCE) 
        {
            goto ResultAtOnce;
        }

        ped->fResultProcess = TRUE;
        if ( ped->fReplaceCompChr ) 
        {
            //
            // we have a DBCS character to be replaced.
            // let's delete it before inserting the new one.
            //
            ich = (ped->fAnsi) ? 2 : 1;
            ped->fReplaceCompChr = FALSE;
            ped->ichMaxSel = min(ped->ichCaret + ich, ped->cch);
            ped->ichMinSel = ped->ichCaret;

            if ( Edit_DeleteText( ped ) > 0 ) 
            {
                if ( ped->fSingle ) 
                {
                    //
                    // Update the display
                    //
                    Edit_NotifyParent(ped, EN_UPDATE);
                    hdc = Edit_GetDC(ped,FALSE);
                    EditSL_DrawText(ped, hdc, 0);
                    Edit_ReleaseDC(ped,hdc,FALSE);
                    //
                    // Tell parent our text contents changed.
                    //
                    Edit_NotifyParent(ped, EN_CHANGE);
                }
            }
            Edit_SetCaretHandler( ped );
        }

    } 
    else if (lParam & CS_INSERTCHAR) 
    {

        //
        // If we are in the middle of a mousedown command, don't do anything.
        //
        if (ped->fMouseDown) 
        {
            return lReturn;
        }

        //
        // We can safely assume that interimm character is always DBCS.
        //
        ich = ( ped->fAnsi ) ? 2 : 1;

        if ( ped->fReplaceCompChr ) 
        {
            //
            // we have a character to be replaced.
            // let's delete it before inserting the new one.
            // when we have a composition characters, the
            // caret is placed before the composition character.
            //
            ped->ichMaxSel = min(ped->ichCaret+ich, ped->cch);
            ped->ichMinSel = ped->ichCaret;
        }

        //
        // let's delete current selected text or composition character
        //
        if ( ped->fSingle ) 
        {
            if ( Edit_DeleteText( ped ) > 0 ) 
            {
                fSLTextUpdated = TRUE;
            }
        } 
        else 
        {
            EditML_DeleteText( ped );
        }

        //
        // When the composition charcter is canceled, IME may give us NULL wParam,
        // with CS_INSERTCHAR flag on. We shouldn't insert a NULL character.
        //
        if ( wParam != 0 ) 
        {

            if ( ped->fAnsi ) 
            {
                TextBuf[0] = HIBYTE(LOWORD(wParam)); // leading byte
                TextBuf[1] = LOBYTE(LOWORD(wParam)); // trailing byte
                TextBuf[2] = '\0';
            } 
            else 
            {
                TextBuf[0] = LOBYTE(LOWORD(wParam));
                TextBuf[1] = HIBYTE(LOWORD(wParam));
                TextBuf[2] = '\0';
                TextBuf[3] = '\0';
            }

            if ( ped->fSingle ) 
            {
                iResult = EditSL_InsertText( ped, (LPSTR)TextBuf, ich );
                if (iResult == 0) 
                {
                    //
                    // Couldn't insert the text, for e.g. the text exceeded the limit.
                    //
                    MessageBeep(0);
                } 
                else if (iResult > 0) 
                {
                    //
                    // Remember we need to update the text.
                    //
                    fSLTextUpdated = TRUE;
                }

            } 
            else 
            {
                iResult = EditML_InsertText( ped, (LPSTR)TextBuf, ich, TRUE);
            }

            if ( iResult > 0 ) 
            {
                //
                // ped->fReplaceCompChr will be reset:
                //
                // 1) when the character is finalized.
                //    we will receive GCS_RESULTSTR
                //
                // 2) when the character is canceled.
                //
                //    we will receive WM_IME_COMPOSITION|CS_INSERTCHAR
                //    with wParam == 0 (in case of user types backspace
                //    at the first element of composition character).
                //
                //      or
                //
                //    we will receive WM_IME_ENDCOMPOSITION message
                //
                ped->fReplaceCompChr = TRUE;

                //
                // Caret should be placed BEFORE the composition
                // character.
                //
                ped->ichCaret = max( 0, (INT)ped->ichCaret - ich);
                Edit_SetCaretHandler( ped );
            } 
            else 
            {
                //
                // We failed to insert a character. We might run out
                // of memory, or reached to the text size limit. let's
                // cancel the composition character.
                //
                hImc = ImmGetContext(ped->hwnd);
                ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
                ImmReleaseContext(ped->hwnd, hImc);

                ped->fReplaceCompChr = FALSE;
                Edit_SetCaretHandler( ped );
            }
        } 
        else 
        {
            //
            // the composition character is canceled.
            //
            ped->fReplaceCompChr = FALSE;
            Edit_SetCaretHandler( ped );
        }

        //
        // We won't notify parent the text change
        // because the composition character has
        // not been finalized.
        //
        if ( fSLTextUpdated ) 
        {
            //
            // Update the display
            //
            Edit_NotifyParent(ped, EN_UPDATE);

            hdc = Edit_GetDC(ped,FALSE);

            if ( ped->fReplaceCompChr ) 
            {
                //
                // move back the caret to the original position
                // temporarily so that our new block cursor can
                // be located within the visible area of window.
                //
                ped->ichCaret = min( ped->cch, ped->ichCaret + ich);
                EditSL_ScrollText(ped, hdc);
                ped->ichCaret = max( 0, (INT)ped->ichCaret - ich);
            } 
            else 
            {
                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);
        }
        return lReturn;
    }

    return DefWindowProc(ped->hwnd, WM_IME_COMPOSITION, wParam, lParam);
}


//---------------------------------------------------------------------------//
//
// HanjaKeyHandler
//
// VK_HANJA handler - Korean only
//
BOOL HanjaKeyHandler(PED ped)
{
    BOOL changeSelection = FALSE;

    if (ped->fKorea && !ped->fReadOnly) 
    {
        ICH oldCaret = ped->ichCaret;

        if (ped->fReplaceCompChr)
        {
            return FALSE;
        }

        if (ped->ichMinSel < ped->ichMaxSel)
        {
            ped->ichCaret = ped->ichMinSel;
        }

        if (!ped->cch || ped->cch == ped->ichCaret) 
        {
            ped->ichCaret = oldCaret;
            MessageBeep(MB_ICONEXCLAMATION);
            return FALSE;
        }

        if (ped->fAnsi) 
        {
            if (ImmEscapeA(GetKeyboardLayout(0), ImmGetContext(ped->hwnd),
                IME_ESC_HANJA_MODE, (Edit_Lock(ped) + ped->ichCaret * ped->cbChar))) 
            {
                changeSelection = TRUE;
            }
            else
            {
                ped->ichCaret = oldCaret;
            }

            Edit_Unlock(ped);
        }
        else 
        {
            if (ImmEscapeW(GetKeyboardLayout(0), ImmGetContext(ped->hwnd),
                IME_ESC_HANJA_MODE, (Edit_Lock(ped) + ped->ichCaret * ped->cbChar))) 
            {
                changeSelection = TRUE;
            }
            else
            {
                ped->ichCaret = oldCaret;
            }

            Edit_Unlock(ped);
        }
    }

    return changeSelection;
}



//---------------------------------------------------------------------------//
// Edit_RequestHandler()
//
// Handles WM_IME_REQUEST message originated by IME
//

#define MAX_ECDOCFEED 20


ICH Edit_ImeGetDocFeedMin(PED ped, LPSTR lpstr)
{
    ICH ich;


    if (ped->ichMinSel > MAX_ECDOCFEED) 
    {
        ich = ped->ichMinSel - MAX_ECDOCFEED;
        ich = Edit_AdjustIch(ped, lpstr, ich);
    } 
    else 
    {
        ich = 0;
    }

    return ich;
}

ICH Edit_ImeGetDocFeedMax(PED ped, LPSTR lpstr)
{
    ICH ich;

    if ((ped->cch - ped->ichMaxSel) > MAX_ECDOCFEED) 
    {
        ich = ped->ichMaxSel + MAX_ECDOCFEED;
        ich = Edit_AdjustIch(ped, lpstr, ich);
    } 
    else 
    {
        ich = ped->cch;
    }

    return ich;
}

LRESULT Edit_RequestHandler(PED ped, WPARAM dwSubMsg, LPARAM lParam)
{
    LRESULT lreturn = 0L;

    switch (dwSubMsg) 
    {
    case IMR_CONFIRMRECONVERTSTRING:

        //
        // CHECK VERSION of the structure
        //
        if (lParam && ((LPRECONVERTSTRING)lParam)->dwVersion != 0) 
        {
            TraceMsg(TF_STANDARD, "Edit_RequestHandler: RECONVERTSTRING dwVersion is not expected.",
                ((LPRECONVERTSTRING)lParam)->dwVersion);
            return 0L;
        }

        if (lParam && ped && ped->fFocus && ped->hText && ImmIsIME(GetKeyboardLayout(0))) 
        {
            LPVOID lpSrc;
            lpSrc = Edit_Lock(ped);
            if (lpSrc == NULL) 
            {
                TraceMsg(TF_STANDARD, "Edit_RequestHandler: LOCALLOCK(ped) failed.");
            } 
            else 
            {
                LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)lParam;
                ICH ichStart;
                ICH ichEnd;
                UINT cchLen;

                ichStart = Edit_ImeGetDocFeedMin(ped, lpSrc);
                ichEnd = Edit_ImeGetDocFeedMax(ped, lpSrc);
                UserAssert(ichEnd >= ichStart);

                cchLen = ichEnd - ichStart;    // holds character count.

                Edit_Unlock(ped);

                if (lpRCS->dwStrLen != cchLen) 
                {
                    TraceMsg(TF_STANDARD, "Edit_RequestHandler: the given string length is not expected.");
                } 
                else 
                {
                    ICH ichSelStart;
                    ICH ichSelEnd;

                    ichSelStart = ichStart + (lpRCS->dwCompStrOffset  / ped->cbChar);
                    ichSelEnd = ichSelStart + lpRCS->dwCompStrLen;


                    (ped->fAnsi ? SendMessageA : SendMessageW)(ped->hwnd, EM_SETSEL, ichSelStart, ichSelEnd);

                    lreturn = 1L;
                }
            }
        }
        break;

    case IMR_RECONVERTSTRING:
        //
        // CHECK VERSION of the structure
        //
        if (lParam && ((LPRECONVERTSTRING)lParam)->dwVersion != 0) 
        {
            TraceMsg(TF_STANDARD, "UxEdit: Edit_RequestHandler: RECONVERTSTRING dwVersion is not expected.");

            return 0L;
        }

        if (ped && ped->fFocus && ped->hText && ImmIsIME(GetKeyboardLayout(0))) 
        {
            ICH ichStart;
            ICH ichEnd;
            UINT cchLen;
            UINT cchSelLen;
            LPVOID lpSrc;
            lpSrc = Edit_Lock(ped);
            if (lpSrc == NULL) 
            {
                TraceMsg(TF_STANDARD, "Edit_RequestHandler: LOCALLOCK(ped) failed.");
                return 0L;
            }

            ichStart = Edit_ImeGetDocFeedMin(ped, lpSrc);
            ichEnd = Edit_ImeGetDocFeedMax(ped, lpSrc);
            UserAssert(ichEnd >= ichStart);

            cchLen = ichEnd - ichStart;    // holds character count.
            cchSelLen = ped->ichMaxSel - ped->ichMinSel;    // holds character count.
            if (cchLen == 0) 
            {
                // if we have no selection,
                // just return 0.
                break;
            }

            UserAssert(ped->cbChar == sizeof(BYTE) || ped->cbChar == sizeof(WCHAR));

            // This Edit Control has selection.
            if (lParam == 0) 
            {
                //
                // IME just want to get required size for buffer.
                // cchLen + 1 is needed to reserve room for trailing L'\0'.
                //       ~~~~
                lreturn = sizeof(RECONVERTSTRING) + (cchLen + 1) * ped->cbChar;
            } 
            else 
            {
                LPRECONVERTSTRING lpRCS = (LPRECONVERTSTRING)lParam;
                LPVOID lpDest = (LPBYTE)lpRCS + sizeof(RECONVERTSTRING);

                // check buffer size
                // if the given buffer is smaller than actual needed size,
                // shrink our size to fit the buffer
                if ((INT)lpRCS->dwSize <= sizeof(RECONVERTSTRING) + cchLen * ped->cbChar) 
                {
                    TraceMsg(TF_STANDARD, "UxEdit: Edit_Request: ERR09");
                    cchLen = (lpRCS->dwSize - sizeof(RECONVERTSTRING)) / ped->cbChar - ped->cbChar;
                }

                lpRCS->dwStrOffset = sizeof(RECONVERTSTRING); // buffer begins just after RECONVERTSTRING
                lpRCS->dwCompStrOffset =
                lpRCS->dwTargetStrOffset = (ped->ichMinSel - ichStart) * ped->cbChar; // BYTE count offset
                lpRCS->dwStrLen = cchLen; // TCHAR count
                lpRCS->dwCompStrLen = 
                lpRCS->dwTargetStrLen = cchSelLen; // TCHAR count

                RtlCopyMemory(lpDest,
                              (LPBYTE)lpSrc + ichStart * ped->cbChar,
                              cchLen * ped->cbChar);
                // Null-Terminate the string
                if (ped->fAnsi) 
                {
                    LPBYTE psz = (LPBYTE)lpDest;
                    psz[cchLen] = '\0';
                } 
                else 
                {
                    LPWSTR pwsz = (LPWSTR)lpDest;
                    pwsz[cchLen] = L'\0';
                }
                Edit_Unlock(ped);
                // final buffer size
                lreturn = sizeof(RECONVERTSTRING) + (cchLen + 1) * ped->cbChar;

                Edit_InOutReconversionMode(ped, TRUE);
                Edit_ImmSetCompositionWindow(ped, 0, 0);
            }

        }
        break;
    }

    return lreturn;
}