// Edit.cpp : Implementation of cEdit
#include "stdafx.h"
#include "DecalControls.h"
#include "Edit.h"

/////////////////////////////////////////////////////////////////////////////
// cEdit

#define TIMER_CARET  1
#define TIMER_REPEAT 2

#define END -1

#ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif
#ifndef MAX
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
#endif


cEdit::cEdit()
: m_bAllowCapture( true ), // cyn -- 15/10/2002
m_bCapture( false ),
m_bDrawCaret( false ),
m_bSelecting( false ),
m_nSelStartChar( 0 ),
m_nSelStart( 0 ),
m_nCaretChar( 0 ),
m_nCaret( 0 ),
m_nOffset( 0 ),
m_nTextColor( 0 ),
m_nCaretHeight( 0 )
{
   m_szMargin.cx = 3;
   m_szMargin.cy = 1;
}

void cEdit::checkFont()
{
   if( m_pFont.p == NULL )
   {
      CComPtr< IPluginSite > pPlugin;
      m_pSite->get_PluginSite( &pPlugin );

	  BSTR bstrFontName;
	  pPlugin->get_FontName(&bstrFontName);
      pPlugin->CreateFont( bstrFontName /*_bstr_t( _T( "Times New Roman" ) )*/, 14, 0, &m_pFont );
   }
}

void cEdit::sendTextChange()
{
   long nID;
   m_pSite->get_ID( &nID );

   Fire_Change( nID, _bstr_t( m_strText.c_str() ) );
}

void cEdit::Delete() {
  if (m_nCaretChar == m_nSelStartChar) {
    return;
  }
  m_strText.assign( m_strText.substr(0, MIN(m_nCaretChar, m_nSelStartChar)) + m_strText.substr(MAX(m_nCaretChar, m_nSelStartChar), m_strText.length()) );
  if (m_bPassword) { /* This is bad, someone fix it */
    m_strPass.assign( m_strPass.substr(0, MIN(m_nCaretChar, m_nSelStartChar)) + m_strPass.substr(MAX(m_nCaretChar, m_nSelStartChar), m_strPass.length()) );
  }
  m_nSelStartChar = MIN(m_nCaretChar, m_nSelStartChar);
  put_Caret( m_nSelStartChar );
}

void cEdit::Copy() {
  HGLOBAL hMem;
  HRESULT hr;
  long hWnd;
  IPluginSite *Plug;
  char *lpMem;
  int Start, Length;

  if (m_bPassword) {
    return;
  }

  hr = m_pSite->get_PluginSite(&Plug);
  if (FAILED(hr)) {
    return;
  }
  hr = Plug->get_HWND(&hWnd);
  Plug->Release();
  Plug = NULL;
  if (FAILED(hr)) {
    return;
  }

  if (m_nCaretChar < m_nSelStartChar) {
    Start = m_nCaretChar;
    Length = m_nSelStartChar - Start;
  } else {
    Start = m_nSelStartChar;
    Length = m_nCaretChar - Start;
  }

  hMem = GlobalAlloc(GMEM_MOVEABLE, Length+1);
  if (!hMem) {
    return;
  }

  
  lpMem = (char *)GlobalLock(hMem);
  if (!lpMem) {
    GlobalFree(hMem);
    return;
  }

  /* This is gauranteed to nul-terminate */
  lstrcpyn( lpMem, m_strText.c_str() + Start, Length + 1);

  GlobalUnlock(hMem);

  if (!OpenClipboard((HWND)hWnd)) {
    GlobalFree(hMem);
    return;
  }
  
  EmptyClipboard();
  GetLastError();
  SetClipboardData(CF_TEXT, hMem);
  GetLastError();
  CloseClipboard();
}


void cEdit::Cut() {
  if (!m_bPassword) {
    Copy();
    Delete();
  }
}

// OK, this paste code isn't pretty, but it's functional for now. Look for a full rewrite later!
// cyn, 22/10/2002
void cEdit::Paste() {
  HANDLE hClipboardData;
  char *sData;
  int Count, Length;

  Delete();

  if (!OpenClipboard(NULL)) {
    return;
  }
    
  hClipboardData = GetClipboardData(CF_TEXT);
  
  if (hClipboardData) {
	   sData = (char *)GlobalLock(hClipboardData);
          
     if (sData) {
       Length = lstrlen(sData);
       for (Count=0;Count<Length;Count++) {
         if ((sData[Count] == 13) || (sData[Count] == 10)) {
           break;
         }
       }
	     m_strText.insert(m_strText.begin() + m_nCaretChar, sData, sData + Count);
       if (m_bPassword) {
         m_strPass.insert(m_strPass.begin(), Count, '*');
       }
       put_Caret(m_nCaretChar + Count);
       GlobalUnlock(hClipboardData);
     }
  }
  
  CloseClipboard();
}

int cEdit::Normalize(int Offset) {
  int Length;
  Length = m_strText.length();
  if (Offset == END) {
    return Length;
  } else if (Offset < END) {
    return 0;
  } else if (Offset > Length) {
    return Length;
  }
  return Offset;
}

STDMETHODIMP cEdit::Reformat()
{
   // Just a typical post-resize type of behavior, make sure everythign is still visible as
   // the user likes.
   if( m_nCaretChar > m_strText.length() )
      put_Caret( m_strText.length() );
   else
      // Force a recalculation for scrolling
      put_Caret( m_nCaretChar );

   //put_Caret( m_nCaretChar );

   return S_OK;
}

STDMETHODIMP cEdit::Render( ICanvas *pCanvas )
{
  HDC RenderDC;
  HRESULT hr;

  checkFont();
  
  // Draw the background
  RECT rcPos;
  m_pSite->get_Position( &rcPos );
  
  RECT rcClient = { 0, 0, rcPos.right - rcPos.left, rcPos.bottom - rcPos.top };
  
  if( m_pBackground.p != NULL )
  {
    static POINT ptOff = { 0, 0 };
    m_pBackground->PatBlt( pCanvas, &rcClient, &ptOff );
  }
  
  RECT rcText = { m_szMargin.cx, m_szMargin.cy, rcClient.right - m_szMargin.cx, rcClient.bottom - m_szMargin.cy };
  VARIANT_BOOL bVisible;
  pCanvas->SetClipRect( &rcText, &bVisible );
  
  if( !bVisible )
    // Nothing to draw huzzah!
    return S_OK;
  
	long lFlags = 0;

	if( m_bAA )
		lFlags |= eAA;

	if( m_bOutline )
		lFlags |= eOutlined;

  // Draw the text
  POINT ptText = { -m_nOffset, 0 };
  if( m_strText.length() > 0 )
    if (m_bPassword) {
		m_pFont->DrawTextEx( &ptText, _bstr_t( m_strPass.c_str() ), m_nTextColor, m_nOutlineColor, lFlags, pCanvas );
    } else {
      m_pFont->DrawTextEx( &ptText, _bstr_t( m_strText.c_str() ), m_nTextColor, m_nOutlineColor, lFlags, pCanvas );
    }

    if( m_bCapture && m_bDrawCaret )
    {
      // Draw the caret
      RECT rcCaret = { m_nCaret - m_nOffset, m_szMargin.cy, m_nCaret - m_nOffset + 1, m_szMargin.cy + m_nCaretHeight };
      pCanvas->Fill( &rcCaret, m_nTextColor );
    }

	if( m_bCapture ) // - Haz, fix for the annoying multiple edit selection ghosts
	{
		if (m_nCaretChar != m_nSelStartChar) {
		hr = pCanvas->GetDC(&RenderDC);
		if (SUCCEEDED(hr)) {
			RECT rcSelect;
			rcSelect.top = m_szMargin.cy;
			rcSelect.bottom = m_szMargin.cy + m_nCaretHeight;
			rcSelect.left = m_nSelStart - m_nOffset;
			rcSelect.right = m_nCaret - m_nOffset;
			if (rcSelect.right < rcSelect.left) {
			int t;
			t = rcSelect.left;
			rcSelect.left = rcSelect.right;
			rcSelect.right = t;
			}
	        

			InvertRect(RenderDC, &rcSelect);
			pCanvas->ReleaseDC();



		}
		}
	}

    return S_OK;
}

STDMETHODIMP cEdit::MouseDown( MouseState *pMS )
{
  if( !m_bCapture )
    Capture();
  else
  {
    _ASSERTE(!m_bSelecting);
    checkFont();
    
    // Hit test the character and move to that position
    long nHitChar;
    if(m_bPassword)
      m_pFont->HitTest( _bstr_t( m_strPass.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
    else
      m_pFont->HitTest( _bstr_t( m_strText.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
    
    // Reset the timer
    put_Caret( nHitChar );

    m_bSelecting = true;
  }
  
  return S_OK;
}

STDMETHODIMP cEdit::MouseUp(MouseState *pMS) {
  /* I assume it's possible to trigger a mouse up without a mousedown */
  m_bSelecting = false;
  return S_OK;
}

STDMETHODIMP cEdit::MouseMove(MouseState *pMS) {
  long nHitChar;

  if (!m_bCapture) {
    _ASSERTE(!m_bSelecting);
    return S_OK;
  }

  if (!m_bSelecting) {
    return S_OK;
  }

  checkFont();
  if (m_bPassword) {
    m_pFont->HitTest( _bstr_t( m_strPass.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
  } else {
    m_pFont->HitTest( _bstr_t( m_strText.c_str() ), pMS->client.x + m_nOffset - m_szMargin.cx, &nHitChar );
  }

  put_Caret(nHitChar);

  return S_OK;
}


STDMETHODIMP cEdit::KeyboardChar( KeyState *pKS )
{
  if (pKS->ctrl && pKS->vkey == 22) {
    Paste();
  } else if (pKS->ctrl && pKS->vkey == 3) {
    Copy();
  } else if (pKS->ctrl && pKS->vkey == 24) {
    Cut();
  } else {
    // Look for special characters
    switch( pKS->vkey )
    {
    case VK_BACK:
      if (m_nCaretChar != m_nSelStartChar) {
        Delete();
      } else {
        if( m_nCaretChar > 0 )
        {
          // Decrease the caret position
          put_Caret( m_nCaretChar - 1 );
        
          // Remove the character
          m_strText.erase( m_strText.begin() + m_nCaretChar, m_strText.begin() + m_nCaretChar + 1 );
        
          if(m_bPassword)
            m_strPass.erase( m_strPass.begin() + m_nCaretChar, m_strPass.begin() + m_nCaretChar + 1 );
        }
      }
      break;
    default:
      // Assume every other character is being added
      if (m_nCaretChar != m_nSelStartChar) {
        Delete();
      }
      char wc[ 2 ] = { pKS->vkey, L'\0' };
      m_strText.insert( m_strText.begin() + m_nCaretChar, wc, wc + 1 );
	     if(m_bPassword)
       {
         wc[0] = L'*';
         m_strPass.insert( m_strPass.begin() + m_nCaretChar, wc, wc + 1 );
       }
       put_Caret( m_nCaretChar + 1 );
    }
  }
  
  m_pSite->Invalidate();
  sendTextChange();
  
  return S_OK;
}

STDMETHODIMP cEdit::KeyboardEndCapture( VARIANT_BOOL bCancel )
{
  if (bCancel) { // cyn, 15/10/2002
     m_bAllowCapture = false;
   }
   m_bCapture = false;
   m_pSite->EndTimer( TIMER_CARET );
   m_pSite->Invalidate();

   long nID;
   m_pSite->get_ID( &nID );
   Fire_End( nID, ( bCancel ) ? VARIANT_FALSE : VARIANT_TRUE );
   m_bAllowCapture = true;  // cyn, 15/10/2002

   return S_OK;
}

STDMETHODIMP cEdit::KeyboardEvent( long nMsg, long wParam, long lParam )
{
  const char *Start;
  int Search;
  bool Shift, OldSelecting;
  int Length;

  if( nMsg != WM_KEYDOWN )
    // Only looking for keydown messages
    return S_OK;

  Shift = (GetAsyncKeyState(VK_SHIFT) < 0);
  OldSelecting = m_bSelecting;
  m_bSelecting |= Shift; /* Hrrmm.. */
  
  // Look for special characters
  switch( wParam )
  {
  case VK_DELETE:
    if (GetAsyncKeyState( VK_SHIFT ) < 0) {
      Cut();
    } else {
      if (m_nCaretChar != m_nSelStartChar) {
        Delete();
      } else {
        if( m_nCaretChar < m_strText.length() ) {
          // Remove the character
          m_strText.erase( m_strText.begin() + m_nCaretChar, m_strText.begin() + m_nCaretChar + 1 );
          if(m_bPassword)
            m_strPass.erase( m_strPass.begin() + m_nCaretChar, m_strPass.begin() + m_nCaretChar + 1 );
        }
      }
    }
    break;
    
  case VK_HOME:
    put_Caret( 0 );
    break;
    
  case VK_END:
    put_Caret( END );
    break;
    
  case VK_INSERT: // cyn, 22/10/2002
    if (Shift) {
      m_bSelecting = false;
      Paste();
    } else if (GetAsyncKeyState( VK_CONTROL) < 0 ) {
      Copy();
    }
    break;
    
  case VK_LEFT:
    if( m_nCaretChar > 0 ) {
      if (GetAsyncKeyState( VK_CONTROL ) < 0) {
        if (m_bPassword) {
          Start = m_strPass.c_str();
        } else {
          Start = m_strText.c_str();
        }
        
        /* Loop until we find a non-space */
        for (Search=m_nCaretChar-1;Search>0;Search--) {
          if (Start[Search] != ' ') {
            break;
          }
        }
        
        /* Loop until we find a space */
        for (;Search>0;Search--) {
          if (Start[Search] == ' ') {
            Search++;
            break;
          }
        }
        
        if (Search < 0) {
          _ASSERTE( false );
          Search = 0;
        }
        
        put_Caret( Search );
      } else {
        put_Caret( m_nCaretChar - 1 );
      }
    }
    break;
    
    
  case VK_RIGHT:
    Length = m_strText.length();
    if( m_nCaretChar < Length ) {
      if (GetAsyncKeyState( VK_CONTROL ) < 0) {
        /* Possible change this, AC edit box is slightly different */
        if (m_bPassword) {
          Start = m_strPass.c_str();
        } else {
          Start = m_strText.c_str();
        }
        
        /* Loop until we find a non-space */
        for (Search=m_nCaretChar+1;Search<Length;Search++) {
          if (Start[Search] != ' ') {
            break;
          }
        }
        
        /* Loop until we find a space */
        for (;Search<Length;Search++) {
          if (Start[Search] == ' ') {
            break;
          }
        }
        
        /* Loop until we find a non-space again, test it in notepad if you don't believe me */
        for (;Search<Length;Search++) {
          if (Start[Search] != ' ') {
            break;
          }
        }
        
        /* It's acceptable to go one character over the end (Search == Length) */
        if (Search > Length) {
          _ASSERTE( false );
          Search = END;
        }
        
        put_Caret( Search );
      } else {
        put_Caret( m_nCaretChar + 1 );
      }
    }
    break;
    
  default:
    // No processing
    m_bSelecting = OldSelecting;
    return S_OK;
   }
   
   m_bSelecting = OldSelecting;
   m_pSite->Invalidate();
   sendTextChange();
   
   return S_OK;
}

STDMETHODIMP cEdit::get_Text(BSTR *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = T2BSTR( m_strText.c_str() );

	return S_OK;
}

STDMETHODIMP cEdit::put_Text(BSTR newVal)
{
   USES_CONVERSION;

   m_strText = OLE2T( newVal );
   
   m_strPass.assign("");

   if(m_bPassword) {
     m_strPass.append(m_strText.length(), '*');
   }

   m_nSelStartChar = m_nCaretChar = 0;

	m_pSite->Invalidate();

	return S_OK;
}

STDMETHODIMP cEdit::get_Caret(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_nCaretChar;

	return S_OK;
}

// Only scroll within 10 pixels of the edge
#define EDIT_HYSTERESIS 18

STDMETHODIMP cEdit::put_Caret(long newVal)
{
  std::string strSample;

  checkFont();

  m_nCaretChar = Normalize(newVal);

  if(m_bPassword) {
    _ASSERTE( m_strPass.length() == m_strText.length() );
    strSample.assign(m_strPass.begin(), m_strPass.begin() + m_nCaretChar);
  } else {
    strSample.assign(m_strText.begin(), m_strText.begin() + m_nCaretChar);
  }

     
  // Calculate the actual character position
  SIZE szText;

  if (!SUCCEEDED (m_pFont->MeasureText( _bstr_t( strSample.c_str() ), &szText )))
  {
    return E_FAIL;
  }
  
  m_nCaretHeight = szText.cy;
  m_nCaret = szText.cx;


  RECT rcPosition;
  m_pSite->get_Position( &rcPosition );
  long nWidth = ( rcPosition.right - rcPosition.left ) - m_szMargin.cx * 2 - 2;
  
  // Check for offset updates
  if( m_nCaret < ( m_nOffset + EDIT_HYSTERESIS ) )
  {
    // The caret has moved in front of the scroll region
    m_nOffset = m_nCaret - EDIT_HYSTERESIS;
    if( m_nOffset < 0 )
      // The offset is less thant the beginning, correct it
      m_nOffset = 0;
  }
  else if( m_nCaret > nWidth + m_nOffset )
    // The caret has moved off the end of the visible region
    // NOTE: the +1 is to account for the 1 pixel width of the caret
    m_nOffset = m_nCaret - nWidth;
  
  if( m_bCapture )
  {
    // Force the caret on for a half second to make sure the user sees the new position
    m_pSite->EndTimer( TIMER_CARET );
    m_pSite->StartTimer( TIMER_CARET, 500 );
    m_bDrawCaret = true;
  }

  if (m_bSelecting) {
    if(m_bPassword) {
      strSample.assign(m_strPass.begin(), m_strPass.begin() + m_nSelStartChar);
    } else {
      strSample.assign(m_strText.begin(), m_strText.begin() + m_nSelStartChar);
    }


    if (!SUCCEEDED (m_pFont->MeasureText( _bstr_t( strSample.c_str() ), &szText))) {
      return E_FAIL;
    }

    m_nSelStart  = szText.cx;
  
  
  } else {
    m_nSelStartChar = m_nCaretChar;
    m_nSelStart = m_nCaret;
  }
  
  m_pSite->Invalidate();
  
  return S_OK;
}

/*
nEnd < nStart is a valid condition
 */
STDMETHODIMP cEdit::Select(long nStart, long nEnd)
{
	bool OldSelection;
  OldSelection = m_bSelecting;
  m_bSelecting = false;
  put_Caret(Normalize(nStart));
  m_bSelecting = true;
  put_Caret(Normalize(nEnd));
  m_bSelecting = OldSelection;

	return S_OK;
}

/* I think this is done */
STDMETHODIMP cEdit::get_SelectedText(BSTR *pVal) {
  int Start, Len;

	_ASSERTE(pVal);

  if (m_bPassword) {
    return S_FALSE;
  }

  if (m_nCaretChar < m_nSelStartChar) {
    Start = m_nCaretChar;
    Len = m_nSelStartChar - Start;
  } else {
    Start = m_nSelStartChar;
    Len = m_nCaretChar - Start;
  }
  *pVal = T2BSTR( m_strText.substr(Start, Len).c_str() );

  return S_OK;
}

STDMETHODIMP cEdit::put_SelectedText(BSTR newVal) {
  USES_CONVERSION;
  std::string strInsert;
  int Length;

  if (m_nCaretChar != m_nSelStartChar) {
    Delete();
  }

  strInsert = OLE2T(newVal);
  Length = strInsert.length();

  m_strText.insert(m_strText.begin() + m_nCaretChar, strInsert.begin(), strInsert.begin() + Length);
  if (m_bPassword) {
    m_strPass.insert(m_strPass.begin(), Length, '*');
  }

  put_Caret( m_nCaretChar + Length );

  m_pSite->Invalidate();
  
	return S_OK;
}

STDMETHODIMP cEdit::TimerTimeout(long nID, long, long, VARIANT_BOOL *pbContinue)
{
   if( nID == TIMER_CARET )
   {
      m_bDrawCaret = !m_bDrawCaret;
      m_pSite->Invalidate();

      *pbContinue = VARIANT_TRUE;
   }

   return S_OK;
}

STDMETHODIMP cEdit::SchemaLoad(IView *pView, IUnknown *pSchema)
{
   USES_CONVERSION;

   // Load the font and background image
   CComPtr< IPluginSite > pPlugin;
   m_pSite->get_PluginSite( &pPlugin );

   pPlugin->CreateFontSchema( 14, 0, pSchema, &m_pFont );
   pPlugin->LoadImageSchema( pSchema, &m_pBackground );

   MSXML::IXMLDOMElementPtr pElement = pSchema;

   _variant_t vText = pElement->getAttribute( _T( "text" ) ),
      vTextColor = pElement->getAttribute( _T( "textcolor" ) ),
      vMarginX = pElement->getAttribute( _T( "marginx" ) ),
      vMarginY = pElement->getAttribute( _T( "marginy" ) ),
	  vPassword = pElement->getAttribute( _T( "password" ) ),
      vOutline = pElement->getAttribute( _T( "outlinecolor" ) ),
	  vAntialias = pElement->getAttribute( _T( "aa" ) );

   if( vText.vt != VT_NULL )
   {
      _ASSERTE( vText.vt == VT_BSTR );
      m_strText = OLE2T( vText.bstrVal );
   }

   if( vTextColor.vt != VT_NULL )
   {
      try
      {
         m_nTextColor = static_cast< long >( vTextColor );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vMarginX.vt != VT_NULL )
   {
      try
      {
         m_szMargin.cx = static_cast< long >( vMarginX );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vMarginY.vt != VT_NULL )
   {
      try
      {
         m_szMargin.cy = static_cast< long >( vMarginY );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vPassword.vt != VT_NULL)
   {
	   try
	   {
		   m_bPassword = static_cast< bool >( vPassword );
	   }
	   catch( ... )
	   {
		   _ASSERTE( FALSE );
	   }
   }
   else
	   m_bPassword = false;

   if( vText.vt != VT_NULL )
   {
      _ASSERTE( vText.vt == VT_BSTR );
      m_strText = OLE2T( vText.bstrVal );
	  
	  if(m_bPassword)
		  for(int i=0; i<m_strText.length(); i++)
			m_strPass.append("*");
   }

	m_nOutlineColor = 0;
	m_bOutline = false;
	if( vOutline.vt != VT_NULL )
	{
		try
		{
			m_bOutline = true;
			m_nOutlineColor = static_cast< long >( vOutline );
		}
		catch( ... )
		{
			// Type conversion error
			_ASSERTE( FALSE );
		}
	}

	m_bAA = true;
	if( vAntialias.vt != VT_NULL )
	{
		try
		{
			m_bAA = static_cast< bool >( vAntialias );
		}
		catch( ... )
		{
			// Type conversion error
			_ASSERTE( FALSE );
		}
	}

   return S_OK;
}

STDMETHODIMP cEdit::get_Background(IImageCacheDisp **pVal)
{
   _ASSERTE( pVal != NULL );

   if( m_pBackground.p == NULL )
      *pVal = NULL;
   else
      m_pBackground->QueryInterface( pVal );

	return S_OK;
}

STDMETHODIMP cEdit::putref_Background(IImageCacheDisp *newVal)
{
   if( m_pBackground.p )
      m_pBackground.Release();

   if( newVal != NULL )
   {
      HRESULT hRes = newVal->QueryInterface( &m_pBackground );
      _ASSERTE( SUCCEEDED( hRes ) );
   }

   m_pSite->Invalidate();

	return S_OK;
}

STDMETHODIMP cEdit::get_Font(IFontCacheDisp **pVal)
{
   _ASSERTE( pVal != NULL );

   if( m_pFont.p == NULL )
      *pVal = NULL;
   else
      m_pFont->QueryInterface( pVal );

	return S_OK;
}

STDMETHODIMP cEdit::putref_Font(IFontCacheDisp *newVal)
{
   _ASSERTE( newVal != NULL );

   if( m_pFont.p )
      m_pFont.Release();

   HRESULT hRes = newVal->QueryInterface( &m_pFont );

   _ASSERTE( SUCCEEDED( hRes ) );

   // Reformat
   put_Caret( m_nCaretChar );

	return S_OK;
}

STDMETHODIMP cEdit::get_TextColor(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_nTextColor;

	return S_OK;
}

STDMETHODIMP cEdit::put_TextColor(long newVal)
{
   _ASSERTE( ( newVal & 0xFF000000 ) == 0 );

   m_nTextColor = newVal;
   m_pSite->Invalidate();

	return S_OK;
}

STDMETHODIMP cEdit::SetMargins(long nX, long nY)
{
   _ASSERTE( nX >= 0 && nY >= 0 );

   m_szMargin.cx = nX;
   m_szMargin.cy = nY;
   put_Caret( m_nCaretChar );

	return S_OK;
}

STDMETHODIMP cEdit::Capture()
{
  _ASSERTE( !m_bCapture );

  if ( m_bAllowCapture ) {
    m_pSite->CaptureKeyboard();
    
    /* Until we actually capture the keyboard, other code can still execute */
    /* If that other code calls put_Caret (eg, put_SelectedText), the timer is set */
    /* If that timer is set, and we then try to set it again, it has an error */
    m_bCapture = true;
    m_bDrawCaret = true;

    m_pSite->Invalidate();
    m_pSite->StartTimer( TIMER_CARET, 500 );

    if (m_nCaretChar == m_nSelStartChar) {
      Select(0, END);
    }

    long nID;
    m_pSite->get_ID( &nID );
    Fire_Begin( nID );

  	return S_OK;
  } else {
    return S_FALSE;
  }
}

