/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  EDITSL.C
 *  Win16 edit control code
 *
 *  History:
 *
 *  Created 28-May-1991 by Jeff Parsons (jeffpar)
 *  Copied from WIN31 and edited (as little as possible) for WOW16.
--*/

/****************************************************************************/
/* editsl.c - Edit controls rewrite.  Version II of edit controls.          */
/*                                                                          */
/*                                                                          */
/* Created:  24-Jul-88 davidds                                              */
/****************************************************************************/

#define  NO_LOCALOBJ_TAGS
#include "user.h"
#include "edit.h"

/****************************************************************************/
/*                      Single Line Support Routines                        */
/****************************************************************************/

void FAR PASCAL SLSetCaretPosition(ped,hdc)
register PED ped;
HDC          hdc;
/* effects: If the window has the focus, find where the caret belongs and move
 * it there.  
 */
{
  int xPosition;
  /* We will only position the caret if we have the focus since we don't want
   * to move the caret while another window could own it.  
   */
  if (ped->fNoRedraw || !ped->fFocus)
      return;

  xPosition = SLIchToLeftXPos(ped, hdc, ped->ichCaret);
  if (!ped->fAutoHScroll)
      /* Don't leet caret go out of bounds of edit contol if there is too much
       * text in a non scrolling edit control. 
       */
      xPosition = min(xPosition, ped->rcFmt.right-1);

  SetCaretPos(xPosition, ped->rcFmt.top);
}


int NEAR PASCAL SLIchToLeftXPos(ped, hdc, ich)
register PED     ped;
HDC              hdc;
ICH              ich;
/* effects: Given a character index, find its (left side) x coordinate within
 * the ped->rcFmt rectangle assuming the character ped->screenStart is at
 * coordinates (ped->rcFmt.top, ped->rcFmt.left).  A negative value is
 * returned if the character ich is to the left of ped->screenStart.  WARNING:
 * ASSUMES AT MOST 1000 characters will be VISIBLE at one time on the screen.
 * There may be 64K total characters in the editcontrol, but we can only
 * display 1000 without scrolling.  This shouldn't be a problem obviously.  
 */
{
  int   textExtent;
  register PSTR pText;

  /* Check if we are adding lots and lots of chars. A paste for example could
   * cause this and GetTextExtents could overflow on this.  
   */
  if (ich > ped->screenStart && ich - ped->screenStart > 1000)
      return(30000);
  if (ped->screenStart > ich && ped->screenStart - ich > 1000)
      return(-30000);

  if (ped->fNonPropFont)
      return((ich-ped->screenStart)*ped->aveCharWidth + ped->rcFmt.left);

  /* Check if password hidden chars are being used. */
  if (ped->charPasswordChar)
      return((ich-ped->screenStart)*ped->cPasswordCharWidth+ped->rcFmt.left);
    
  pText = LocalLock(ped->hText);

  if (ped->screenStart <= ich)
    {
      textExtent = LOWORD(GetTextExtent(hdc,
                                    (LPSTR)(pText + ped->screenStart),
                                    ich-ped->screenStart));
      /* In case of signed/unsigned overflow since the text extent may be
       * greater than maxint.  This happens with long single line edit
       * controls. The rect we edit text in will never be greater than 30000
       * pixels so we are ok if we just ignore them.  
       */
      if (textExtent < 0 || textExtent > 31000)
          textExtent = 30000;
    }
  else
      textExtent = (-1) * 
                         (int)LOWORD(GetTextExtent(hdc,(LPSTR)(pText + ich),
                                              ped->screenStart-ich));

  LocalUnlock(ped->hText);

  return(textExtent-ped->charOverhang + ped->rcFmt.left);
}

/* effects: This finds out if the given ichPos falls within the current
 * Selection range and if so returns TRUE; Else returns FALSE.  
 */
BOOL NEAR PASCAL SLGetHiliteAttr(PED ped, ICH ichPos)
{
  return((ichPos >= ped->ichMinSel) && (ichPos < ped->ichMaxSel));
}

/* effects: This takes care of erasing the old selection and drawing the new
 * selection 
 */
void NEAR PASCAL SLRepaintChangedSelection(
	PED ped, HDC hdc,
	ICH ichOldMinSel, ICH ichOldMaxSel)
{
  BLOCK Blk[2];
  int   i;

  Blk[0].StPos = ichOldMinSel;
  Blk[0].EndPos = ichOldMaxSel;
  Blk[1].StPos = ped->ichMinSel;
  Blk[1].EndPos = ped->ichMaxSel;

  if(ECCalcChangeSelection(ped, ichOldMinSel, ichOldMaxSel, 
                           (LPBLOCK)&Blk[0], (LPBLOCK)&Blk[1]))
  {
     UpdateWindow(ped->hwnd);
     /* Paint the rectangles where selection has changed */
     /* Paint both Blk[0] and Blk[1], if they exist */
     for(i = 0; i < 2; i++)
     {
        if (Blk[i].StPos != -1)
            SLDrawLine(ped, hdc, Blk[i].StPos, Blk[i].EndPos - Blk[i].StPos,
                       SLGetHiliteAttr(ped, Blk[i].StPos));
     }
  }
}


void FAR PASCAL SLChangeSelection(ped, hdc, ichNewMinSel, ichNewMaxSel)
register PED ped;
HDC          hdc;
ICH          ichNewMinSel;
ICH          ichNewMaxSel;
/* effects: Changes the current selection to have the specified starting and
 * ending values.  Properly highlights the new selection and unhighlights
 * anything deselected.  If NewMinSel and NewMaxSel are out of order, we swap
 * them. Doesn't update the caret position.  
 */
{
  ICH temp;
  ICH  ichOldMinSel;
  ICH  ichOldMaxSel;

  if (ichNewMinSel > ichNewMaxSel)
    {
      temp = ichNewMinSel;
      ichNewMinSel = ichNewMaxSel;
      ichNewMaxSel = temp;
    }
    ichNewMinSel = umin(ichNewMinSel, ped->cch);
    ichNewMaxSel = umin(ichNewMaxSel, ped->cch);

  /* Preserve the Old selection */
  ichOldMinSel = ped->ichMinSel;
  ichOldMaxSel = ped->ichMaxSel;

  /* Set new selection */
  ped->ichMinSel = ichNewMinSel;
  ped->ichMaxSel = ichNewMaxSel;

  /* We will find the intersection of current selection rectangle with the new
   * selection rectangle.  We will then invert the parts of the two rectangles
   * not in the intersection.  
   */

  if (!ped->fNoRedraw && (ped->fFocus || ped->fNoHideSel))
    {
      if (ped->fFocus)
          HideCaret(ped->hwnd);
      SLRepaintChangedSelection(ped, hdc, ichOldMinSel, ichOldMaxSel);
      SLSetCaretPosition(ped,hdc);
      if (ped->fFocus)
          ShowCaret(ped->hwnd);
    }
}


void NEAR PASCAL SLDrawLine(ped, hdc, ichStart, iCount, fSelStatus)

register PED   ped;
register HDC   hdc;
ICH            ichStart;
int            iCount;
BOOL           fSelStatus;

/* This draws the line starting from ichStart, iCount number of characters;
 * fSelStatus is TRUE, if it is to be drawn with the "selection" attribute.  
 */
{
  RECT   rc;
  HBRUSH hBrush;
  PSTR   pText;
  DWORD  rgbSaveBk;
  DWORD  rgbSaveText;
  int    iStCount;
  DWORD  rgbGray=0;

  if (ped->fNoRedraw)
      return;

  if (ichStart < ped->screenStart)
    {
      if (ichStart+iCount < ped->screenStart)
          return;

      iCount = iCount - (ped->screenStart-ichStart);
      ichStart = ped->screenStart;
    }

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

  /* Set the proper clipping rectangle */
  ECSetEditClip(ped, hdc);

  pText = (PSTR) LocalLock(ped->hText);

  /* Calculates the rectangle area to be wiped out */
  if (iStCount = ichStart - ped->screenStart)
    {
      if (ped->charPasswordChar)
          rc.left += ped->cPasswordCharWidth * iStCount;
      else
          rc.left += LOWORD(GetTextExtent(hdc, 
                                          (LPSTR)(pText + ped->screenStart),
                                          iStCount)) -
                     ped->charOverhang;
    }

  if (ped->charPasswordChar)
      rc.right = rc.left + ped->cPasswordCharWidth * iCount;
  else
      rc.right = rc.left + LOWORD(GetTextExtent(hdc, 
                                            (LPSTR)(pText + ichStart), 
                                            iCount));
  /* Set the background mode before calling GetControlBrush so that the app
   * can change it to TRANSPARENT if it wants to.  
   */
  SetBkMode(hdc, OPAQUE);

  if (fSelStatus)
    {
      hBrush = ped->hbrHiliteBk;
      rgbSaveBk = SetBkColor(hdc, ped->rgbHiliteBk);
      rgbSaveText = SetTextColor(hdc, ped->rgbHiliteText);
    }
  else
    {
      /* We always want to send this so that the app has a chance to muck with
       * the DC 
       */
      hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT);
    }

  if (ped->fDisabled && 
      (rgbGray = GetSysColor(COLOR_GRAYTEXT)))
    {
      /* Grey the text in the edit control if disabled. 
       */
      rgbSaveText = SetTextColor(hdc, rgbGray);
    }

  /* Erase the rectangular area before text is drawn. Note that we inflate the
   * rect by 1 so that the selection color has a one pixel border around the
   * text. 
   */
  InflateRect((LPRECT)&rc, 0, 1);
  /* Use paint rect so that the brush gets aligned if dithered. 
   */
  PaintRect(ped->hwndParent, ped->hwnd, hdc, hBrush, (LPRECT)&rc);
  InflateRect((LPRECT)&rc, 0, -1);
  
  if (ped->charPasswordChar)
    {
      for (iStCount = 0; iStCount < iCount; iStCount++)
           TextOut(hdc,
                   rc.left+iStCount*ped->cPasswordCharWidth,
                   rc.top,
                   (LPSTR)&ped->charPasswordChar,
                   1);
    }
  else
      TextOut(hdc,rc.left,rc.top,(LPSTR)(pText+ichStart),iCount);

  if (fSelStatus || rgbGray)
    {
      SetTextColor(hdc, rgbSaveText);
      if (fSelStatus)
          SetBkColor(hdc, rgbSaveBk);

    }

  LocalUnlock(ped->hText);
}

int  NEAR PASCAL SLGetBlkEnd(ped, ichStart, ichEnd, lpfStatus)
register  PED  ped;
register  ICH  ichStart;
ICH            ichEnd;
BOOL FAR       *lpfStatus;
/* Given a Starting point and and end point, this function returns whether the
 * first few characters fall inside or outside the selection block and if so,
 * howmany characters?  
 */
{
    *lpfStatus = FALSE;
    if (ichStart >= ped->ichMinSel)
      {
        if(ichStart >= ped->ichMaxSel)
            return(ichEnd - ichStart);
        *lpfStatus = TRUE;
        return(min(ichEnd, ped->ichMaxSel) - ichStart);
      }
    return(min(ichEnd, ped->ichMinSel) - ichStart);
}

void NEAR PASCAL SLDrawText(ped, hdc, ichStart)
register PED   ped;
register HDC   hdc;
ICH            ichStart;
/* effects:  Draws text for a single line edit control in the rectangle
 * specified by ped->rcFmt.  If ichStart == 0, starts drawing text at the left
 * side of the window starting at character index ped->screenStart and draws
 * as much as will fit.  If ichStart > 0, then it appends the characters
 * starting at ichStart to the end of the text showing in the window. (ie. We
 * are just growing the text length and keeping the left side
 * (ped->screenStart to ichStart characters) the same.  Assumes the hdc came
 * from ECGetEditDC so that the caret and such are properly hidden.  
 */
{
  ICH    cchToDraw;
  RECT   rc;

  PSTR   pText;
  BOOL   fSelStatus;
  int    iCount;
  ICH    ichEnd;
  BOOL   fNoSelection;

  if (ped->fNoRedraw)
      return;

  if (ichStart == 0)
      ichStart = ped->screenStart;

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

  /* Find out how many characters will fit on the screen so that we don't do
   * any needless drawing.  
   */

  pText = (PSTR) LocalLock(ped->hText);

  cchToDraw = ECCchInWidth(ped, hdc, 
                           (LPSTR)(pText+ped->screenStart),
                           ped->cch-ped->screenStart,
                           rc.right - rc.left);
  ichEnd = ped->screenStart + cchToDraw;

  /*
   *  There is no selection if,
   *     1. MinSel and MaxSel are equal  OR
   *     2. (This has lost the focus AND Selection is to be hidden)
   */
  fNoSelection = ((ped->ichMinSel == ped->ichMaxSel) ||
                  (!ped->fFocus && !ped->fNoHideSel));
  while (ichStart < ichEnd)
    {
        if (fNoSelection)
          {
            fSelStatus = FALSE;
            iCount = ichEnd - ichStart;
          }
        else
            iCount = SLGetBlkEnd(ped, ichStart, ichEnd, 
		                 (BOOL FAR *)&fSelStatus);
			 
        SLDrawLine(ped, hdc, ichStart, iCount, fSelStatus);
        ichStart += iCount;
    }

  if (cchToDraw)
    {
      /* Check if password hidden chars are being used. */
      if (ped->charPasswordChar)
          rc.left += ped->cPasswordCharWidth * cchToDraw; 
      else 
          rc.left += LOWORD(GetTextExtent(hdc, 
                          (LPSTR)(pText + ped->screenStart), cchToDraw));
    }

  LocalUnlock(ped->hText);

  /* Check if anything to be erased on the right hand side */
  if (rc.left < rc.right)
    {
      SetBkMode(hdc, OPAQUE);
      /* Erase the rectangular area before text is drawn. Note that we inflate
       * the rect by 1 so that the selection color has a one pixel border
       * around the text. 
       */
      InflateRect((LPRECT)&rc, 0, 1);
      PaintRect(ped->hwndParent, ped->hwnd, hdc, NULL, (LPRECT)&rc);
    }
            
  SLSetCaretPosition(ped, hdc);
}


BOOL FAR PASCAL SLScrollText(ped, hdc)
register PED  ped;
HDC           hdc;
/* effects: Scrolls the text to bring the caret into view. If the text is
 * scrolled, the current selection is unhighlighted.  Returns TRUE if the text
 * is scrolled else returns false.  
 */
{
  register PSTR pText;
  ICH scrollAmount = (ped->rcFmt.right-ped->rcFmt.left)/4/ped->aveCharWidth+1;
  ICH newScreenStartX = ped->screenStart;

  if (!ped->fAutoHScroll)
      return(FALSE);

  /* Calculate the new starting screen position */
  if (ped->ichCaret <= ped->screenStart)
    {
      /* Caret is to the left of the starting text on the screen we must
       * scroll the text backwards to bring it into view.  Watch out when
       * subtracting unsigned numbers when we have the possibility of going
       * negative.  
       */
      if (ped->ichCaret > scrollAmount)
          newScreenStartX = ped->ichCaret - scrollAmount;
      else
          newScreenStartX = 0;
    }
  else
    {
      pText = (PSTR) LocalLock(ped->hText);
      if ((ped->ichCaret != ped->screenStart) &&
          (ECCchInWidth(ped, hdc, (LPSTR)(pText+ped->screenStart),
                  ped->ichCaret - ped->screenStart, 
                  ped->rcFmt.right-ped->rcFmt.left)  < 
           ped->ichCaret - ped->screenStart))
        {
           newScreenStartX = ((ped->ichCaret < scrollAmount*2) ? 0 :
                               ped->ichCaret - scrollAmount*2);
        }
      LocalUnlock(ped->hText);
    }

#ifdef DBCS
  pText = (PSTR) LocalLock(ped->hText);
  newScreenStartX = ECAdjustIch( ped,pText,newScreenStartX );
  LocalUnlock(ped->hText);
#endif

  if (ped->screenStart != newScreenStartX)
    {
      ped->screenStart = newScreenStartX;
      SLDrawText(ped, hdc, 0);
      /* Caret pos is set by drawtext */
      return(TRUE);
    }

  return(FALSE);

}


ICH FAR PASCAL SLInsertText(ped, lpText, cchInsert)
register PED   ped;
LPSTR          lpText;
register ICH   cchInsert;
/* effects: Adds up to cchInsert characters from lpText to the ped starting at
 * ichCaret. If the ped only allows a maximum number of characters, then we
 * will only add that many characters to the ped and send a EN_MAXTEXT
 * notification code to the parent of the ec.  Also, if !fAutoHScroll, then we
 * only allow as many chars as will fit in the client rectangle. The number of
 * characters actually added is returned (could be 0). If we can't allocate
 * the required space, we notify the parent with EN_ERRSPACE and no characters
 * are added.  
 */
{
  HDC  hdc;
  PSTR pText;
  ICH  cchInsertCopy = cchInsert;
  int  textWidth;

  /* First determine exactly how many characters from lpText we can insert
   * into the ped.  
   */
  if (!ped->fAutoHScroll)
    {
      pText = (PSTR)LocalLock(ped->hText);
      hdc = ECGetEditDC(ped, TRUE);

      /* If ped->fAutoHScroll bit is not set, then we only insert as many
       * characters as will fit in the ped->rcFmt rectangle upto a maximum of
       * ped->cchTextMax - ped->cch characters. Note that if password style is
       * on, we allow the user to enter as many chars as the number of
       * password chars which fit in the rect.  
       */
      if (ped->cchTextMax <= ped->cch)
          cchInsert = 0;
      else
        {
          cchInsert = umin(cchInsert, (unsigned)(ped->cchTextMax - ped->cch));
          if (ped->charPasswordChar)
     	      textWidth = ped->cch * ped->cPasswordCharWidth;
          else
              textWidth = LOWORD(GetTextExtent(hdc, (LPSTR)pText, ped->cch));

          cchInsert = umin(cchInsert,
                       ECCchInWidth(ped, hdc, lpText, cchInsert, 
                                    ped->rcFmt.right-ped->rcFmt.left-
                                    textWidth));
        }

      LocalUnlock(ped->hText);
      ECReleaseEditDC(ped, hdc, TRUE);
    }
  else
    {
      if (ped->cchTextMax <= ped->cch)
          cchInsert = 0;
      else
          cchInsert = umin((unsigned)(ped->cchTextMax - ped->cch), cchInsert);
    }

  /* Now try actually adding the text to the ped */
  if (cchInsert && !ECInsertText(ped, lpText, cchInsert))
    {
      ECNotifyParent(ped, EN_ERRSPACE);
      return(0);
    }

  if (cchInsert)
      ped->fDirty = TRUE; /* Set modify flag */

  if (cchInsert < cchInsertCopy)
      /* Notify parent that we couldn't insert all the text requested */
      ECNotifyParent(ped, EN_MAXTEXT);

  /* Update selection extents and the caret position.  Note that ECInsertText
   * updates ped->ichCaret, ped->ichMinSel, and ped->ichMaxSel to all be after
   * the inserted text.  
   */

  return(cchInsert);
}


ICH PASCAL NEAR SLPasteText(register PED ped)
/* effects: Pastes a line of text from the clipboard into the edit control
 * starting at ped->ichMaxSel.  Updates ichMaxSel and ichMinSel to point to
 * the end of the inserted text.  Notifies the parent if space cannot be
 * allocated. Returns how many characters were inserted.  
 */
{
  HANDLE           hData;
  LPSTR            lpchClip;
  LPSTR            lpchClip2;
  register ICH     cchAdded;
  ICH              clipLength;

  if (!OpenClipboard(ped->hwnd))
      return(0);

  if (!(hData = GetClipboardData(CF_TEXT)))
    {
      CloseClipboard();
      return(0);
    }
    
  lpchClip2 = lpchClip = (LPSTR) GlobalLock(hData);

  /* Find the first carrage return or line feed.  Just add text to that point.
   */
  clipLength = (WORD)lstrlen(lpchClip);
  for (cchAdded = 0; cchAdded < clipLength; cchAdded++)
       if (*lpchClip2++ == 0x0D)
           break;

  /* Insert the text (SLInsertText checks line length) */
  cchAdded = SLInsertText(ped, lpchClip, cchAdded);

  GlobalUnlock(hData);
  CloseClipboard();

  return(cchAdded);
}


void NEAR PASCAL SLReplaceSelHandler(register PED ped,
                                     LPSTR        lpText)
/* effects: Replaces the text in the current selection with the given text. 
 */
{
  BOOL fUpdate;
  HDC  hdc;

  SwapHandle(&lpText);
  ECEmptyUndo(ped);
  fUpdate = (BOOL)ECDeleteText(ped);
  ECEmptyUndo(ped);
  SwapHandle(&lpText);
  fUpdate = (BOOL)SLInsertText(ped, lpText, lstrlen(lpText)) || fUpdate;
  ECEmptyUndo(ped);
  
  if (fUpdate)
    {
      ECNotifyParent(ped, EN_UPDATE);
      hdc = ECGetEditDC(ped,FALSE);
      if (!SLScrollText(ped,hdc))
          SLDrawText(ped,hdc,0);
      ECReleaseEditDC(ped,hdc,FALSE);
      ECNotifyParent(ped,EN_CHANGE);
    }
}



void FAR PASCAL SLCharHandler(ped, keyValue, keyMods)
register PED  ped;
WORD          keyValue;
int           keyMods;
/* effects: Handles character input (really, no foolin') 
 */
{
  register HDC  hdc;
  unsigned char keyPress = LOBYTE(keyValue);
  BOOL          updateText = FALSE;
#ifdef DBCS
  PSTR pText;
  int InsertTextLen = 0;
  WORD DBCSkey;
#endif

  if (ped->fMouseDown || ped->fReadOnly)
      /* Don't do anything if we are in the middle of a mousedown deal or if
       * this is a read only edit control. 
       */
      return;

  if ((keyPress == BACKSPACE) || (keyPress >= ' '))
    {
      /* Delete the selected text if any */
      if (ECDeleteText(ped))
          updateText=TRUE;
    }

  switch(keyPress)
  {
    case BACKSPACE:
      /* Delete any selected text or delete character left if no sel */
      if (!updateText && ped->ichMinSel)
        {
          /* There was no selection to delete so we just delete character 
             left if available */
#ifdef DBCS
          pText = LMHtoP( ped->hText );
          if( ECAnsiPrev( ped,pText, pText+ped->ichMinSel) == pText+ped->ichMinSel-2)
              ped->ichMinSel--;
#endif
          ped->ichMinSel--;
          (void) ECDeleteText(ped);
          updateText = TRUE;
        }
      break;

    default:
      if (keyPress >= ' ')
        {
#ifdef DBCS
          InsertTextLen = 1;
          if( IsDBCSLeadByte( keyPress ) )
              if( ( DBCSkey = DBCSCombine( ped->hwnd, keyPress ) ) != NULL )
                  if( SLInsertText( ped, (LPSTR)&DBCSkey, 2 ) == 2){
                      InsertTextLen = 2;
                      updateText = TRUE;
                  }else
                      MessageBeep(0);
              else
                  MessageBeep(0);
          else
#endif
          if (SLInsertText(ped, (LPSTR) &keyPress, 1))
              updateText = TRUE;
          else
              /* Beep. Since we couldn't add the text */
              MessageBeep(0);
        }
      else
          /* User hit an illegal control key */
          MessageBeep(0);

      break;
  }


  if (updateText) 
    {
      /* Dirty flag (ped->fDirty) was set when we inserted text */
      ECNotifyParent(ped, EN_UPDATE);
      hdc = ECGetEditDC(ped,FALSE);
      if (!SLScrollText(ped,hdc))
#ifdef DBCS
          SLDrawText(ped,hdc,(ped->ichCaret == 0 ? 0 :ped->ichCaret - InsertTextLen));
#else
          SLDrawText(ped,hdc,(ped->ichCaret == 0 ? 0 :ped->ichCaret - 1));
#endif
      ECReleaseEditDC(ped,hdc,FALSE);
      ECNotifyParent(ped,EN_CHANGE);
    }

}



void NEAR PASCAL SLKeyDownHandler(register PED  ped,
                                  WORD          virtKeyCode,
				  int           keyMods)
/* effects: Handles cursor movement and other VIRT KEY stuff.  keyMods allows
 * us to make SLKeyDownHandler calls and specify if the modifier keys (shift
 * and control) are up or down.  This is useful for imnplementing the
 * cut/paste/clear messages for single line edit controls.  If keyMods == 0,
 * we get the keyboard state using GetKeyState(VK_SHIFT) etc. Otherwise, the
 * bits in keyMods define the state of the shift and control keys.  
 */
{
  HDC  hdc;


  /* Variables we will use for redrawing the updated text */
  register ICH newMaxSel = ped->ichMaxSel;
  ICH          newMinSel = ped->ichMinSel;

  /* Flags for drawing the updated text */
  BOOL updateText = FALSE;
  BOOL changeSelection = FALSE; /* new selection is specified by 
                                   newMinSel, newMaxSel */

  /* Comparisons we do often */
  BOOL MinEqMax = (newMaxSel == newMinSel);
  BOOL MinEqCar = (ped->ichCaret == newMinSel);
  BOOL MaxEqCar = (ped->ichCaret == newMaxSel);

  /* State of shift and control keys. */
  int scState = 0; 

  /* Combo box support */
  BOOL fIsListVisible;
  BOOL fIsExtendedUI;

#ifdef DBCS
  PSTR pText;
#endif

  if (ped->fMouseDown)
    {
      /* If we are in the middle of a mouse down handler, then don't do
       * anything. ie. ignore keyboard input.
       */
      return;
    }

  if (!keyMods)
    {
      /* Get state of modifier keys for use later. */
      scState =  ((GetKeyState(VK_CONTROL) & 0x8000) ? 1 : 0); 
      scState += ((GetKeyState(VK_SHIFT) & 0x8000) ? 2 : 0); 
    }
  else
      scState = ((keyMods == NOMODIFY) ? 0 : keyMods);



  switch(virtKeyCode)
  {
    case VK_UP:
      if (ped->listboxHwnd)
        {
          /* Handle Combobox support */
          fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L);
          fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L);

          if (!fIsListVisible && fIsExtendedUI)
            {
              /* For TandyT
	       */
DropExtendedUIListBox:
              /* Since an extendedui combo box doesn't do anything on f4, we
	       * turn off the extended ui, send the f4 to drop, and turn it
	       * back on again.
	       */
              SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 0, 0L);
              SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0L);
              SendMessage(ped->hwndParent, CB_SETEXTENDEDUI, 1, 0L);
              return;
            }
          else
              goto SendKeyToListBox;
        }

      /* else fall through */
      
    case VK_LEFT:
      /* If the caret isn't already at 0, we can move left */
      if (ped->ichCaret)
        {
          switch (scState)
          {
             case NONEDOWN:
               /* Clear selection, move caret left */
#ifdef DBCS
               pText = LMHtoP( ped->hText );
               if( ECAnsiPrev( ped, pText, pText + ped->ichCaret ) ==
                   pText + ped->ichCaret - 2 )
                 ped->ichCaret--;
#endif
               ped->ichCaret--;
               newMaxSel = newMinSel = ped->ichCaret;
               break;

             case CTRLDOWN:
               /* Clear selection, move caret word left */
               ped->ichCaret = LOWORD(ECWord(ped,ped->ichCaret,TRUE));
               newMaxSel = newMinSel = ped->ichCaret;
               break;

             case SHFTDOWN:
               /* Extend selection, move caret left */
#ifdef DBCS
               pText = LMHtoP( ped->hText );
               if( ECAnsiPrev( ped, pText, pText + ped->ichCaret ) ==
                   pText + ped->ichCaret - 2 )
                 ped->ichCaret--;
#endif
               ped->ichCaret--;
               if (MaxEqCar && !MinEqMax)
                   /* Reduce selection extent */
                   newMaxSel = ped->ichCaret;
               else
                   /* Extend selection extent */
                   newMinSel = ped->ichCaret;
               break;

             case SHCTDOWN:
               /* Extend selection, move caret word left */
               ped->ichCaret = LOWORD(ECWord(ped,ped->ichCaret,TRUE));
               if (MaxEqCar && !MinEqMax)
                 {
                   /* Reduce selection extent */
                   /*
                    * Hint: Suppose WORD.  OR is selected. Cursor between R
                    * and D. Hit select word left, we want to just select the
                    * W and leave cursor before the W.
                    */
                   newMinSel = ped->ichMinSel;
                   newMaxSel = ped->ichCaret;
                 }
               else
                   /* Extend selection extent */
                   newMinSel = ped->ichCaret;
               break;
          }
 
          changeSelection = TRUE;
        }
      else
	{
          /* If the user tries to move left and we are at the 0th character 
             and there is a selection, then cancel the selection. */
          if (ped->ichMaxSel != ped->ichMinSel && 
              (scState == NONEDOWN || scState == CTRLDOWN))
            {
              changeSelection = TRUE;
              newMaxSel = newMinSel = ped->ichCaret;
            }
        }
      break;

    case VK_DOWN:
      if (ped->listboxHwnd)
        {
          /* Handle Combobox support */
          fIsExtendedUI = SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L);
          fIsListVisible = SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L);

          if (!fIsListVisible && fIsExtendedUI)
            {
              /* For TandyT
	       */
              goto DropExtendedUIListBox;
            }
          else
              goto SendKeyToListBox;
        }

      /* else fall through */
      
    case VK_RIGHT:
      /*
       * If the caret isn't already at ped->cch, we can move right.
       */
      if (ped->ichCaret < ped->cch)
        {
          switch (scState)
          {
             case NONEDOWN:
               /* Clear selection, move caret right */
#ifdef DBCS
               pText = LMHtoP( ped->hText ) + ped->ichCaret;
               if( ECIsDBCSLeadByte( ped, *pText ) )
                 ped->ichCaret++;
#endif
               ped->ichCaret++;
               newMaxSel = newMinSel = ped->ichCaret;
               break;

             case CTRLDOWN:
               /* Clear selection, move caret word right */
               ped->ichCaret = HIWORD(ECWord(ped,ped->ichCaret,FALSE));
               newMaxSel = newMinSel = ped->ichCaret;
               break;

             case SHFTDOWN:
               /* Extend selection, move caret right */
#ifdef DBCS
               pText = LMHtoP( ped->hText ) + ped->ichCaret;
               if( ECIsDBCSLeadByte( ped, *pText ) )
                 ped->ichCaret++;
#endif
               ped->ichCaret++;
               if (MinEqCar && !MinEqMax)
                   /* Reduce selection extent */
                   newMinSel = ped->ichCaret;
               else
                   /* Extend selection extent */
                   newMaxSel = ped->ichCaret;
               break;

             case SHCTDOWN:
               /* Extend selection, move caret word right */
               ped->ichCaret = HIWORD(ECWord(ped,ped->ichCaret,FALSE));
               if (MinEqCar && !MinEqMax)
                 {
                   /* Reduce selection extent */
                   newMinSel = ped->ichCaret;
                   newMaxSel = ped->ichMaxSel;
                 }
               else
                   /* Extend selection extent */
                   newMaxSel = ped->ichCaret;
               break;
           }

           changeSelection = TRUE;
        }
      else
	{
          /* If the user tries to move right and we are at the last character 
             and there is a selection, then cancel the selection. */
          if (ped->ichMaxSel != ped->ichMinSel &&
              (scState == NONEDOWN || scState == CTRLDOWN))
            {
              newMaxSel = newMinSel = ped->ichCaret;
              changeSelection = TRUE;
            }
        }
      break;

    case VK_HOME:
      ped->ichCaret = 0;
      switch (scState)
        {
           case NONEDOWN:
           case CTRLDOWN:
             /* Clear selection, move caret home */
             newMaxSel = newMinSel = ped->ichCaret;
             break;

           case SHFTDOWN:
           case SHCTDOWN:
             /* Extend selection, move caret home */
             if (MaxEqCar && !MinEqMax)
               {
                 /* Reduce/negate selection extent */
                 newMinSel = 0;
                 newMaxSel = ped->ichMinSel;
               }
             else
                 /* Extend selection extent */
                 newMinSel = ped->ichCaret;
             break;
         }

      changeSelection = TRUE; 
      break;

    case VK_END:
      newMaxSel = ped->ichCaret = ped->cch;
      switch (scState)
        {
           case NONEDOWN:
           case CTRLDOWN:
             /* Clear selection, move caret to end of text */
             newMinSel = ped->cch;
             break;

           case SHFTDOWN:
           case SHCTDOWN:
             /* Extend selection, move caret to end of text */
             if (MinEqCar && !MinEqMax)
                 /* Reduce/negate selection extent */
                 newMinSel = ped->ichMaxSel;
             /* else Extend selection extent */
             break;
         }

      changeSelection = TRUE;
      break;

    case VK_DELETE:
      if (ped->fReadOnly)
          break;

      switch (scState)
        {
           case NONEDOWN:
             /*	Clear selection.  If no selection, delete (clear) character
	      * right.  
	      */
             if ((ped->ichMaxSel<ped->cch) && 
                 (ped->ichMinSel==ped->ichMaxSel))
               {
                 /* Move cursor forwards and simulate a backspace.  
		  */
#ifdef DBCS
                 pText = LMHtoP( ped->hText ) + ped->ichCaret;
                 if( ECIsDBCSLeadByte( ped, *pText ) )
                   ped->ichCaret++;
#endif
                 ped->ichCaret++;
                 ped->ichMaxSel = ped->ichMinSel = ped->ichCaret;
                 SLCharHandler(ped, (WORD) BACKSPACE, 0);
               }
             if (ped->ichMinSel != ped->ichMaxSel)
                 SLCharHandler(ped, (WORD) BACKSPACE, 0);
             break;

           case SHFTDOWN:
             /*	CUT selection ie. remove and copy to clipboard, or if no
	      * selection, delete (clear) character left.  
	      */

             if (SendMessage(ped->hwnd, WM_COPY, (WORD)0,0L) ||
                 (ped->ichMinSel == ped->ichMaxSel))
                 /* If copy successful, delete the copied text by simulating a
		  * backspace message which will redraw the text and take care
		  * of notifying the parent of changes.  Or if there is no
		  * selection, just delete char left.  
		  */
                 SLCharHandler(ped, (WORD) BACKSPACE, 0);
             break;

           case CTRLDOWN:
             /*	Delete to end of line if no selection else delete (clear)
	      * selection. 
	      */
             if ((ped->ichMaxSel<ped->cch) && 
                 (ped->ichMinSel==ped->ichMaxSel))
               {
                 /* Move cursor to end of line and simulate a backspace.  
		  */
                 ped->ichMaxSel = ped->ichCaret = ped->cch;
               }
             if (ped->ichMinSel != ped->ichMaxSel)
                 SLCharHandler(ped, (WORD) BACKSPACE, 0);
             break;
        }
 
      /* No need to update text or selection since BACKSPACE message does it
       * for us.  
       */
      break;

    case VK_INSERT:
      switch (scState)
        {
           case CTRLDOWN:
             /* Copy current selection to clipboard */
             SendMessage(ped->hwnd, WM_COPY, (WORD)NULL, (LONG)NULL);
             break;

           case SHFTDOWN:
             if (ped->fReadOnly)
                 break;

             /* Insert contents of clipboard (PASTE) */
             /* Unhighlight current selection and delete it, if any */
             ECDeleteText(ped);
             SLPasteText(ped);
             updateText = TRUE;
             ECNotifyParent(ped, EN_UPDATE);
             break;
        }
      break;

    case VK_F4:
    case VK_PRIOR:
    case VK_NEXT:
      /* Send keys to the listbox if we are a part of a combo box. This
       * assumes the listbox ignores keyup messages which is correct right
       * now.  
       */
      if (ped->listboxHwnd)
        {
SendKeyToListBox:
          /* Handle Combobox support */
          SendMessage(ped->listboxHwnd, WM_KEYDOWN, virtKeyCode, 0L);
          return;
        }
  }



  if (changeSelection || updateText)
    {
      hdc = ECGetEditDC(ped,FALSE);
      /* Scroll if needed */
      SLScrollText(ped,hdc);

      if (changeSelection)
          SLChangeSelection(ped,hdc,newMinSel,newMaxSel);
      if (updateText)
          SLDrawText(ped,hdc,0);

      /* SLSetCaretPosition(ped,hdc);*/
      ECReleaseEditDC(ped,hdc,FALSE);
      if (updateText)
          ECNotifyParent(ped, EN_CHANGE);
    }

}


ICH NEAR PASCAL SLMouseToIch(ped, hdc, mousePt)
register PED  ped;
HDC           hdc;
POINT         mousePt;
/* effects: Returns the closest cch to where the mouse point is.  
 */
{
  register PSTR pText;
  int           width = mousePt.x;
  int           textWidth;
  ICH           cch;

  if (width <= ped->rcFmt.left)
    {
      /* Return either the first non visible character or return 0 if at
       * beginning of text 
       */
      if (ped->screenStart)
          return(ped->screenStart - 1);
      else
          return(0);
    }

  if (width > ped->rcFmt.right)
    {
      pText = LocalLock(ped->hText);

      /* Return last char in text or one plus the last char visible */
      cch = ECCchInWidth(ped, hdc, (LPSTR)(pText+ped->screenStart), 
                         ped->cch - ped->screenStart, 
                         ped->rcFmt.right-ped->rcFmt.left) + 
            ped->screenStart;
      LocalUnlock(ped->hText);
      if (cch >= ped->cch)
          return(ped->cch);
      else
          return(cch+1);
    }

  /* Check if password hidden chars are being used. */
  if (ped->charPasswordChar)
      return(umin((width-ped->rcFmt.left)/ped->cPasswordCharWidth,ped->cch));

  if (!ped->cch)
      return(0);

  pText = LocalLock(ped->hText);
  cch   = ped->cch;

  while(((textWidth = 
	   LOWORD(GetTextExtent(hdc, (LPSTR)(pText+ped->screenStart), 
                               cch-ped->screenStart)))
           > (width-ped->rcFmt.left)) && (cch-ped->screenStart) )
    {
      /* For that "feel" */
      if ((textWidth - ped->aveCharWidth/2) < (width-ped->rcFmt.left))
          break;

      cch--;
    }

#ifdef DBCS
  cch = ECAdjustIch( ped, pText, cch );
#endif

  LocalUnlock(ped->hText);
  return(cch);
}




void NEAR PASCAL SLMouseMotionHandler(ped, message, virtKeyDown, mousePt)
register PED   ped;
WORD           message;
WORD           virtKeyDown;
POINT          mousePt;
{
  LONG selection;
  BOOL changeSelection = FALSE;

  HDC  hdc = ECGetEditDC(ped,FALSE);
  
  ICH newMaxSel = ped->ichMaxSel;
  ICH newMinSel = ped->ichMinSel;

  ICH mouseIch = SLMouseToIch(ped, hdc, mousePt);

#ifdef DBCS
  LPSTR pText;

  pText = LocalLock( ped->hText );
  mouseIch = ECAdjustIch( ped, pText, mouseIch );
  LocalUnlock( ped->hText );
#endif

  switch (message)
  {
    case WM_LBUTTONDBLCLK:
      /* Note that we don't have to worry about this control having the focus
       * since it got it when the WM_LBUTTONDOWN message was first sent.  If
       * shift key is down, extend selection to word we double clicked on else
       * clear current selection and select word.  
       */
#ifdef DBCS
      pText = LocalLock( ped->hText ) + ped->ichCaret;
      selection = ECWord(ped,ped->ichCaret,
          (ECIsDBCSLeadByte(ped,*pText) && ped->ichCaret < ped->cch) ? FALSE : TRUE );
      LocalUnlock( ped->hText );
#else
      selection = ECWord(ped,ped->ichCaret,TRUE);
#endif
      newMinSel = LOWORD(selection);
      newMaxSel = ped->ichCaret = HIWORD(selection);
      changeSelection = TRUE;
      /* Set mouse down to false so that the caret isn't reposition on the
       * mouseup message or on an accidental move...  
       */
      ped->fMouseDown = FALSE;
      break;

    case WM_MOUSEMOVE:
      if (ped->fMouseDown)
        {
          changeSelection = TRUE;
          /* Extend selection, move caret word right */
          if ((ped->ichMinSel == ped->ichCaret) && 
              (ped->ichMinSel != ped->ichMaxSel))
            {
              /* Reduce selection extent */
              newMinSel = ped->ichCaret = mouseIch;
              newMaxSel = ped->ichMaxSel;
            }
          else
              /* Extend selection extent */
              newMaxSel = ped->ichCaret=mouseIch;
        }
      break;

    case WM_LBUTTONDOWN:  
      /*
       * If we currently don't have the focus yet, try to get it.
       */
      if (!ped->fFocus)
        {
          if (!ped->fNoHideSel)
              /* Clear the selection before setting the focus so that we don't
	       * get refresh problems and flicker. Doesn't matter since the
	       * mouse down will end up changing it anyway.  
	       */
              ped->ichMinSel = ped->ichMaxSel = ped->ichCaret;

          SetFocus(ped->hwnd);
          /* If we are part of a combo box, then this is the first time the
	   * edit control is getting the focus so we just want to highlight
	   * the selection and we don't really want to position the caret.  
	   */
          if (ped->listboxHwnd)
              break;
        }

      if (ped->fFocus)
        {
          /* Only do this if we have the focus since a clever app may not want
	   * to give us the focus at the SetFocus call above.  
	   */
          ped->fMouseDown = TRUE;
          SetCapture(ped->hwnd);
          changeSelection = TRUE;
          if (!(virtKeyDown & MK_SHIFT))
            {
              /*
               * If shift key isn't down, move caret to mouse point and clear
               * old selection
               */
              newMinSel = newMaxSel = ped->ichCaret = mouseIch;
            }
         else
           {
             /*	Shiftkey is down so we want to maintain the current selection
	      * (if any) and just extend or reduce it 
	      */
             if (ped->ichMinSel == ped->ichCaret)
                 newMinSel = ped->ichCaret = mouseIch;
             else
                 newMaxSel = ped->ichCaret = mouseIch;
           }
        }
      break;

    case WM_LBUTTONUP:
      if (ped->fMouseDown)
        {
          ReleaseCapture();
          /*SLSetCaretPosition(ped,hdc);*/
          ped->fMouseDown = FALSE;
        }
      break;
  }


  if (changeSelection)
    {
      SLScrollText(ped,hdc);
      SLChangeSelection(ped, hdc, newMinSel, newMaxSel);
    }

  ECReleaseEditDC(ped,hdc,FALSE);

}




void NEAR PASCAL SLPaintHandler(ped,althdc)
register PED  ped;
HDC           althdc;
/* effects: Handles painting of the edit control window. Draws the border if
 * necessary and draws the text in its current state.  
 */
{
  HWND         hwnd = ped->hwnd;
  HBRUSH       hBrush;
  register HDC hdc;
  PAINTSTRUCT  paintstruct;
  RECT         rcEdit;
  HANDLE       hOldFont;

  /* Had to put in hide/show carets.  The first one needs to be done before
   * beginpaint to correctly paint the caret if part is in the update region
   * and part is out.  The second is for 1.03 compatibility.  It breaks
   * micrografix's worksheet edit control if not there.  
   */

  HideCaret(hwnd);

  /* Allow subclassing hdc */
  if (!althdc)
      hdc = BeginPaint(hwnd, (PAINTSTRUCT FAR *)&paintstruct);
  else
      hdc = althdc;

  HideCaret(hwnd);

  if (!ped->fNoRedraw && IsWindowVisible(ped->hwnd))
    {
      /* Erase the background since we don't do it in the erasebkgnd message.
       */
      hBrush = GetControlBrush(ped->hwnd, hdc, CTLCOLOR_EDIT);
      FillWindow(ped->hwndParent, hwnd, hdc, hBrush);

      if (ped->fBorder)
        {
          GetClientRect(hwnd, (LPRECT)&rcEdit);
          DrawFrame(hdc, (LPRECT)&rcEdit, 1, DF_WINDOWFRAME);
        }

      if (ped->hFont)
          /* We have to select in the font since this may be a subclassed dc
	   * or a begin paint dc which hasn't been initialized with out fonts
	   * like ECGetEditDC does.  
	   */
          hOldFont = SelectObject(hdc, ped->hFont);

      SLDrawText(ped, hdc, 0);

      if (ped->hFont && hOldFont)
          SelectObject(hdc, hOldFont);

    }
  ShowCaret(hwnd);

  if (!althdc)
      EndPaint(hwnd, (LPPAINTSTRUCT)&paintstruct);

  ShowCaret(hwnd);
}



void NEAR PASCAL SLSetFocusHandler(ped)
register PED  ped;
/* effects: Gives the edit control the focus and notifies the parent
 * EN_SETFOCUS.  
 */
{
  register HDC hdc;

  if (!ped->fFocus)
    {
      UpdateWindow(ped->hwnd);

      ped->fFocus = TRUE;  /* Set focus */

      /* We don't want to muck with the caret since it isn't created. */
      hdc = ECGetEditDC(ped,TRUE);

      /* Show the current selection.  Only if the selection was hidden when we
       * lost the focus, must we invert (show) it.  
       */
      if (!ped->fNoHideSel)
          SLDrawText(ped, hdc, 0);		

      /* Create the caret. Add in the +1 because we have an extra pixel for
       * highlighting around the text. If the font is at least as wide as the
       * system font, use a wide caret else use a 1 pixel wide caret. 
       */
      CreateCaret(ped->hwnd, (HBITMAP)NULL, 
                  (ped->cxSysCharWidth > ped->aveCharWidth ? 1 : 2), 
                  ped->lineHeight+1);
      SLSetCaretPosition(ped,hdc);
      ECReleaseEditDC(ped,hdc,TRUE);
      ShowCaret(ped->hwnd);
    }

  /* Notify parent we have the focus */
  ECNotifyParent(ped, EN_SETFOCUS);
}




void NEAR PASCAL SLKillFocusHandler(ped, newFocusHwnd)
register PED ped;
HWND         newFocusHwnd;
/* effects: The edit control loses the focus and notifies the parent via
 * EN_KILLFOCUS.  
 */
{
  RECT rcEdit;

  if (ped->fFocus)
    {
      /* Destroy the caret */
      HideCaret(ped->hwnd);
      DestroyCaret();

      ped->fFocus = FALSE;  /* Clear focus */

      /* Do this only if we still have the focus.  But we always notify the
       * parent that we lost the focus whether or not we originally had the
       * focus.  
       */
      /* Hide the current selection if needed */
      if (!ped->fNoHideSel && (ped->ichMinSel != ped->ichMaxSel))
        {
          GetClientRect(ped->hwnd, (LPRECT)&rcEdit);
          if (ped->fBorder && rcEdit.right-rcEdit.left && 
              rcEdit.bottom-rcEdit.top)
            {
              /* Don't invalidate the border so that we avoid flicker */
              InflateRect((LPRECT)&rcEdit, -1, -1);
            }
          InvalidateRect(ped->hwnd, (LPRECT)&rcEdit, FALSE);
          UpdateWindow(ped->hwnd);

#if 0
          SLSetSelectionHandler(ped, ped->ichCaret, ped->ichCaret);
#endif
        }
    }

  /* If we aren't a combo box, notify parent that we lost the focus.  
   */
  if (!ped->listboxHwnd)
      ECNotifyParent(ped, EN_KILLFOCUS);
  else
    {
      /* This editcontrol is part of a combo box and is losing the focus.  If
       * the focus is NOT being sent to another control in the combo box
       * window, then it means the combo box is losing the focus.  So we will
       * notify the combo box of this fact.  
       */
      if (!IsChild(ped->hwndParent, newFocusHwnd))
        {
          /* Focus is being sent to a window which is not a child of the combo
	   * box window which implies that the combo box is losing the focus.
	   * Send a message to the combo box informing him of this fact so
	   * that he can clean up...  
	   */
          SendMessage(ped->hwndParent, CBEC_KILLCOMBOFOCUS, 0, 0L);
        }
    }
}


/*******************/
/* SLEditWndProc() */
/*******************/
LONG FAR PASCAL SLEditWndProc(hwnd, ped, message, wParam, lParam)
HWND          hwnd;
register PED  ped;
WORD          message;
register WORD wParam;
LONG          lParam;
/* effects: Class procedure for all single line edit controls.
        Dispatches all messages to the appropriate handlers which are named 
        as follows:
        SL (single line) prefixes all single line edit control procedures while
        EC (edit control) prefixes all common handlers.

        The SLEditWndProc only handles messages specific to single line edit
        controls.
 */

{
  /* Dispatch the various messages we can receive */
  switch (message)
  {
    case WM_CLEAR:
      /* wParam - not used
         lParam - not used */
      /*
       * Call SLKeyDownHandler with a VK_DELETE keycode to clear the selected
       * text.
       */
      if (ped->ichMinSel != ped->ichMaxSel)
          SLKeyDownHandler(ped, VK_DELETE, NOMODIFY);
      break;

    case WM_CHAR:
      /* wParam - the value of the key
         lParam - modifiers, repeat count etc (not used) */
      if (!ped->fEatNextChar)
          SLCharHandler(ped, wParam, 0);
      else
	  ped->fEatNextChar = FALSE;    
      break;

    case WM_CUT:
      /* wParam - not used
         lParam - not used */
      /* Call SLKeyDownHandler with a VK_DELETE keycode to cut the selected
       * text. (Delete key with shift modifier.) This is needed so that apps
       * can send us WM_PASTE messages.  
       */
      if (ped->ichMinSel != ped->ichMaxSel)
          SLKeyDownHandler(ped, VK_DELETE, SHFTDOWN);
      break;

    case WM_ERASEBKGND:
      /* wParam - device context handle
         lParam - not used */
      /* We do nothing on this message and we don't want DefWndProc to do
       * anything, so return 1 
       */
      return(1L);
      break;

    case WM_GETDLGCODE:
      /* wParam - not used
         lParam - not used */
      return(DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS);
      break;

    case WM_KEYDOWN:
      /* wParam - virt keycode of the given key
         lParam - modifiers such as repeat count etc. (not used) */
      SLKeyDownHandler(ped, wParam, 0);
      break;

    case WM_KILLFOCUS:
      /* wParam - handle of the window that receives the input focus
         lParam - not used */
      SLKillFocusHandler(ped, (HWND)wParam);
      break;

    case WM_MOUSEMOVE:
    case WM_LBUTTONDBLCLK:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
      /* wParam - contains a value that indicates which virtual keys are down
         lParam - contains x and y coords of the mouse cursor */
      SLMouseMotionHandler(ped, message, wParam, MAKEPOINT(lParam));
      break;

    case WM_CREATE:
      /* wParam - handle to window being created
         lParam - points to a CREATESTRUCT that contains copies of parameters 
                  passed to the CreateWindow function. */
      return(SLCreateHandler(hwnd, ped, (LPCREATESTRUCT) lParam));
      break;

    case WM_PAINT:
      /* wParam - not used - actually sometimes used as a hdc when subclassing 
         lParam - not used */
      SLPaintHandler(ped, wParam);
      break;

    case WM_PASTE:
      /* wParam - not used
         lParam - not used */
      /* Call SLKeyDownHandler with a SHIFT VK_INSERT keycode to paste the
       * clipboard into the edit control.  This is needed so that apps can
       * send us WM_PASTE messages.  
       */
      SLKeyDownHandler(ped, VK_INSERT, SHFTDOWN);
      break;

    case WM_SETFOCUS:
      /* wParam - handle of window that loses the input focus (may be NULL)
         lParam - not used */
      SLSetFocusHandler(ped);
      break;

    case WM_SETTEXT:
      /* wParam - not used
         lParam - points to a null-terminated string that is used to set the
                  window text. */
      return(SLSetTextHandler(ped, (LPSTR)lParam));
      break;

    case WM_SIZE:
      /* wParam - defines the type of resizing fullscreen, sizeiconic, 
	          sizenormal etc.
         lParam - new width in LOWORD, new height in HIGHWORD of client area */
      SLSizeHandler(ped);
      return(0L);
      break;

    case WM_SYSKEYDOWN:
      if (ped->listboxHwnd && /* Check if we are in a combo box */
          (lParam & 0x20000000L))  /* Check if the alt key is down */
        {
          /*
	   * Handle Combobox support. We want alt up or down arrow to behave
	   * like F4 key which completes the combo box selection
	   */
          ped->fEatNextChar = FALSE;		
          if (lParam & 0x1000000)
            {
              /* This is an extended key such as the arrow keys not on the
	       * numeric keypad so just drop the combobox. 
	       */
              if (wParam == VK_DOWN || wParam == VK_UP)
                  goto DropCombo;
              else
                  goto foo;
            }

          if (GetKeyState(VK_NUMLOCK) < 0)
	    {
	      ped->fEatNextChar = FALSE;
              /* If numlock down, just send all system keys to dwp */
 	      goto foo;
            }
          else
              /* Num lock is up. Eat the characters generated by the key board
	       * driver. 
	       */
	      ped->fEatNextChar = TRUE;

	  if (!(wParam == VK_DOWN || wParam == VK_UP))
              goto foo;

DropCombo:
          if (SendMessage(ped->hwndParent, CB_GETEXTENDEDUI, 0, 0L) & 0x00000001)
            {
              /* Extended ui doesn't honor VK_F4. */
              if (SendMessage(ped->hwndParent, CB_GETDROPPEDSTATE, 0, 0L))
                  return(SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 0, 0L));
              else
                  return(SendMessage(ped->hwndParent, CB_SHOWDROPDOWN, 1, 0L));
            }
          else
              return(SendMessage(ped->listboxHwnd, WM_KEYDOWN, VK_F4, 0L));
        }
foo:	
      if (wParam == VK_BACK)
	{
          SendMessage(ped->hwnd, EM_UNDO, 0, 0L);
          break;
        }
      goto PassToDefaultWindowProc;
      break;

    case EM_GETLINE:
      /* wParam - line number to copy (always the first line for SL)
         lParam - buffer to copy text to. FIrst word is max # of bytes to copy
       */
      return(ECGetTextHandler(ped, (*(WORD FAR *)lParam), (LPSTR)lParam));
      break;

    case EM_LINELENGTH:
      /* wParam - ignored
         lParam - ignored */
      return((LONG)ped->cch);
      break;

    case EM_SETSEL:
      /* wParam - not used
         lParam - starting pos in lowword ending pos in high word */
      SLSetSelectionHandler(ped, LOWORD(lParam), HIWORD(lParam));
      break;

    case EM_REPLACESEL:
      /* wParam - not used
         lParam - points to a null terminated string of replacement text */
      SLReplaceSelHandler(ped, (LPSTR)lParam);
      break;

    case WM_UNDO:
    case EM_UNDO:
      SLUndoHandler(ped);
      break;
	    
    default:
PassToDefaultWindowProc:
      return(DefWindowProc(hwnd,message,wParam,lParam));
      break;

  } /* switch (message) */

  return(1L);
} /* SLEditWndProc */
