/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

/* COMPFRM.CPP - Compose Window Frame.  This module contains code for the
 * compose window frame class.  Handles all menu and tool commands.
 *
 */
 
#include "stdafx.h"
#include "edt.h"
#include "compbar.h"
#include "compfrm.h"
#include "prefapi.h"
#include "intl_csi.h"

extern "C" {
#include "xpgetstr.h"
extern int MK_MSG_SAVE_AS;
extern int MK_MSG_CANT_OPEN;
extern int MK_MSG_MISSING_SUBJECT;
};

#ifdef XP_WIN32
#include "postal.h"
#endif

#define COMPOSE_WINDOW_WIDTH    73 // default to 73 characters wide

/////////////////////////////////////////////////////////////////////////////
// CComposeFrame

// This is for mail that is sent immediately
static UINT ComposeCodes[] = {
   IDM_SENDNOW,
   IDM_QUOTEORIGINAL,
   IDM_ADDRESSPICKER,
   ID_CHECK_SPELLING,
   IDM_SAVEASDRAFT,
   ID_SECURITY,
   ID_NAVIGATE_INTERRUPT
    };

static BOOL bSendDeferred = FALSE;

BEGIN_MESSAGE_MAP(CComposeFrame, CGenericFrame)
   ON_WM_CREATE()
   ON_WM_CLOSE()
   ON_WM_SETFOCUS()
   ON_MESSAGE(WM_SETMESSAGESTRING, OnSetMessageString)
   ON_MESSAGE(NSBUTTONMENUOPEN, OnButtonMenuOpen)
   ON_MESSAGE(WM_TOOLCONTROLLER,OnToolController)
   ON_COMMAND(ID_CHECK_SPELLING, OnCheckSpelling)
   ON_COMMAND(IDM_PASTEASQUOTE, OnPasteQuote)
   ON_UPDATE_COMMAND_UI(IDM_PASTEASQUOTE, OnUpdatePasteQuote)
   ON_COMMAND(ID_EDIT_SELECTALLMESSAGES, OnSelectAll)
   ON_UPDATE_COMMAND_UI(ID_EDIT_SELECTALLMESSAGES,OnUpdateSelectAll)
   ON_COMMAND(IDM_SEND, OnSend)
   ON_UPDATE_COMMAND_UI(IDM_SEND, OnUpdateSend)
   ON_COMMAND(IDM_SENDNOW, OnSendNow)
   ON_UPDATE_COMMAND_UI(IDM_SENDNOW, OnUpdateSendNow)
   ON_COMMAND(IDM_SENDLATER, OnSendLater)
   ON_UPDATE_COMMAND_UI(IDM_SENDLATER,OnUpdateSendLater)
   ON_COMMAND(IDM_QUOTEORIGINAL, OnQuoteOriginal)
   ON_UPDATE_COMMAND_UI(IDM_QUOTEORIGINAL,OnUpdateButtonGeneral)
   ON_COMMAND(IDM_SAVEAS, OnSaveAs)
   ON_UPDATE_COMMAND_UI(IDM_SAVEAS, OnUpdateButtonGeneral)
   ON_COMMAND(IDM_SAVEASDRAFT, OnSaveDraft)
   ON_UPDATE_COMMAND_UI(IDM_SAVEASDRAFT, OnUpdateSaveDraft)
   ON_COMMAND(IDM_SAVEASTEMPLATE, OnSaveTemplate)
   ON_UPDATE_COMMAND_UI(IDM_SAVEASTEMPLATE, OnUpdateSaveTemplate)
   ON_COMMAND(IDM_CONVERT,OnConvert)
   ON_UPDATE_COMMAND_UI(IDM_CONVERT, OnUpdateConvert)
   ON_COMMAND(IDM_ATTACHFILE,OnAttachFile)
   ON_COMMAND(ID_SECURITY,OnSecurity)
   ON_UPDATE_COMMAND_UI(ID_SECURITY,OnUpdateSecurity)
   
   ON_COMMAND(ID_DONEGOINGOFFLINE, OnDoneGoingOffline)

   ON_WM_DESTROY()
   ON_BN_CLICKED(IDM_ATTACHFILE, OnAttachFile)
   ON_BN_CLICKED(IDM_ATTACHWEBPAGE, OnAttachUrl)
   ON_COMMAND(IDM_ADDRESSPICKER,OnSelectAddresses)
   ON_COMMAND(ID_MAIL_WRAPLONGLINES,OnWrapLongLines)
   ON_UPDATE_COMMAND_UI(ID_MAIL_WRAPLONGLINES,OnUpdateWrapLongLines)    
   ON_COMMAND(IDM_ATTACHMYCARD,OnAttachMyCard)
   ON_UPDATE_COMMAND_UI(IDM_ATTACHMYCARD,OnUpdateAttachMyCard)
   ON_COMMAND(ID_FILE_NEWMESSAGE, OnNew)
   ON_COMMAND(IDM_ADDRESSES,OnViewAddresses)
   ON_COMMAND(IDM_ATTACHMENTS,OnViewAttachments)
   ON_COMMAND(IDM_OPTIONS,OnViewOptions)
   ON_COMMAND(IDC_ADDRESSTAB,OnAddressTab)
   ON_COMMAND(IDC_ATTACHTAB,OnAttachTab)
   ON_COMMAND(IDC_OPTIONSTAB,OnOptionsTab)
   ON_COMMAND(IDC_COLLAPSE,OnCollapse)
   ON_UPDATE_COMMAND_UI(ID_CHECK_SPELLING, OnUpdateButtonGeneral)
   ON_UPDATE_COMMAND_UI(IDM_ADDRESSES,OnUpdateViewAddresses)
   ON_UPDATE_COMMAND_UI(IDM_ATTACHMENTS,OnUpdateViewAttachments)
   ON_UPDATE_COMMAND_UI(IDM_OPTIONS,OnUpdateViewOptions)
   ON_REGISTERED_MESSAGE(WM_FINDREPLACE, OnFindReplace)
   // general button enabler, these should be enabled as long as the
   // compose window is visible
   ON_UPDATE_COMMAND_UI(ID_VIEW_ADDRESSES, OnUpdateButtonGeneral)
   ON_UPDATE_COMMAND_UI(IDC_ADDRESS,OnUpdateButtonGeneral)
   ON_UPDATE_COMMAND_UI(IDC_DIRECTORY,OnUpdateButtonGeneral)
   ON_UPDATE_COMMAND_UI(IDM_ATTACHFILE,OnUpdateButtonGeneral) 
   ON_UPDATE_COMMAND_UI(IDM_ATTACHWEBPAGE,OnUpdateButtonGeneral)
   ON_UPDATE_COMMAND_UI(IDM_ADDRESSPICKER,OnUpdateButtonGeneral) 

   ON_COMMAND(IDM_MESSAGEBODYONLY,OnToggleAddressArea)
   ON_UPDATE_COMMAND_UI(IDM_MESSAGEBODYONLY,OnUpdateToggleAddressArea)
   ON_COMMAND(IDM_OPT_MESSAGEBAR_TOGGLE, OnToggleMessageToolbar)
   ON_UPDATE_COMMAND_UI(IDM_OPT_MESSAGEBAR_TOGGLE, OnUpdateToggleMessageToolbar)

	// Security Stuff 
   	ON_UPDATE_COMMAND_UI(IDS_SECURITY_STATUS, OnUpdateSecureStatus)
	ON_UPDATE_COMMAND_UI(IDS_SIGNED_STATUS, OnUpdateSecureStatus)


END_MESSAGE_MAP()

#ifndef _AFXDLL
#undef new
#endif
IMPLEMENT_DYNCREATE(CComposeFrame,CGenericFrame)
#ifndef _AFXDLL
#define new DEBUG_NEW
#endif

#ifdef XP_WIN32
char *ToolBarProfileName = "MailComposeToolBar";
#endif

// Constructor for CCompFrame.  Creates the toolbar controller object.
// This object is used by the Gold editor to display/control the
// HTML toolbar commands.  Initialize local variables, set the deferred
// flag to the last known state

CComposeFrame::CComposeFrame()
{
	PREF_GetBoolPref("network.online", &bSendDeferred);
   bSendDeferred = !bSendDeferred;
	m_bUseHtml = FALSE;
	m_pToolBarController = NULL;
   m_pComposeBar = NULL;                               // dialog bar
	m_pComposePane = NULL;                              // message pane (for backend)
   m_pFocus = NULL;                                    // current field with focus
   m_bInitialized = FALSE;
	m_pInitialText = NULL;
   m_bWrapLongLines = TRUE;
   m_cxChar = 0;
   m_quoteSel = -1;				// no quoting selection yet

   m_bMAPISendMode = MAPI_IGNORE;   // rhp - For MAPI UI-less send/save operations
}

// The menu gets cleaned up on window destruction but we need
// to delete our accelerator table ourselves

CComposeFrame::~CComposeFrame()
{
    // free the accelerator table
    if(m_hAccelTable != NULL)   {
    	::FreeResource((HGLOBAL)m_hAccelTable);
    	m_hAccelTable = NULL;
    }
    if (m_pToolBarController)
    	delete m_pToolBarController;
    // free up the dialog bar (address & attachment controls)
    if (m_pComposeBar)
    	delete m_pComposeBar;
}


// GetAddressWidget() returns the current address control widget.

LPADDRESSCONTROL CComposeFrame::GetAddressWidgetInterface() 
{
    ASSERT(m_pComposeBar);
    return m_pComposeBar->GetAddressWidgetInterface();
}    

// AppendAddress() adds a header/value to the current address block.  It 
// accepts a MSG_HEADER_SET value which is translated to the appropriate 
// corresponding string and appended to the end of the address list with 
// the supplied value.
// Returns TRUE on success, FALSE otherwise.

BOOL CComposeFrame::AppendAddress(
    MSG_HEADER_SET header,  // header to add
    const char * value      // header value (or NULL for empty)
    )
{
    // check for a valid widget 
    if (::IsWindow(m_pComposeBar->m_hWnd))
    {
	    // get a pointer to the addressing block widget
        LPADDRESSCONTROL pIAddress = GetAddressWidgetInterface();
        if (pIAddress)
        {
            CListBox * pListBox = pIAddress->GetListBox();	    
    	    // check to make sure we got a valid list and the list
    	    // has a valid window
    	    if (::IsWindow(pListBox->m_hWnd))
    	    {
                const char * szType = MSG_HeaderMaskToString(header);
                if (!szType)
                    return FALSE;
    	        // add the entry to the list
                pIAddress->AppendEntry(TRUE, szType,value ? value : "", 0, 0);
    	    }
        }
    }
    return TRUE;
}

// destroy message compose pane
void CComposeFrame::OnDestroy()
{
    MSG_DestroyPane(m_pComposePane);
    m_pComposePane = NULL;
    CGenericFrame::OnDestroy();
}

int CComposeFrame::CreateHtmlToolbars()
{
   // Create the HTML edit toolbars.  There are currently two separate
   // toolbars.. one for formats and another for character operations.
   m_pToolBarController = new CEditToolBarController(this);
   ASSERT(m_pToolBarController);
   if (!m_pToolBarController->CreateEditBars(GetMainContext()->GetContext(), DISPLAY_CHARACTER_TOOLBAR))
      return -1;      // fail to create
   return 0;
}

void CComposeFrame::DestroyHtmlToolbars()
{
   XP_Bool prefBool;
   PREF_GetBoolPref("editor.show_character_toolbar",&prefBool);
   if ( prefBool ) 
   {
      CWnd * pWnd = m_pToolBarController->GetCharacterBar();
      if (pWnd && ::IsWindow(pWnd->m_hWnd))
         pWnd->DestroyWindow();
   }
}

void CComposeFrame::ShowHtmlToolbars()
{
   // display the appropriate toolbars for HTML editing based on 
   // configuration.
   XP_Bool prefBool;
   PREF_GetBoolPref("editor.show_character_toolbar",&prefBool);
   if ( prefBool ) 
      m_pToolBarController->GetCharacterBar()->ShowWindow(SW_SHOW);
}

void CComposeFrame::SetMsgPane(MSG_Pane * pPane) 
{ 
    m_pComposePane = pPane; 
}

void CComposeFrame::CreatePlainTextEditor()
{
   CWnd *pView = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
   ASSERT(pView);

   // create the editor parent.  This is a window whose sole purpose is to
   // resize the edit control... this will go away when the Gold editor
   // is fully integrated.

   CRect rect(0,0,0,0);   
   if (::IsWindow(pView->m_hWnd) && pView->IsWindowVisible())
      pView->GetClientRect(rect);
   
   m_EditorParent.Create ( 
      NULL, NULL, WS_VISIBLE | WS_CHILD,  rect, pView, 0 );        

   SetEditorParent((CWnd*)&m_EditorParent);

   // create the editor which is currently a standard windows edit 
   // control.  This will be replaced by the Gold editor.

   if (rect.Width())
      rect.left = EDIT_MARGIN_OFFSET;

   m_Editor.Create ( 
      WS_VSCROLL | ES_MULTILINE | WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_WANTRETURN | ES_NOHIDESEL,
      rect, 
      &m_EditorParent, IDC_COMPOSEEDITOR);
      
   m_Editor.SetComposeFrame(this);
   m_Editor.SetTabStops();

   m_cxChar = m_Editor.GetCharWidth();

   #ifdef XP_WIN16
   m_Editor.SendMessage(EM_LIMITTEXT,0,0);
   #endif
}

// Perform initial setup for the compose window.

// status bar format
static const UINT BASED_CODE indicators[] =
{
	IDS_SECURITY_STATUS,
	IDS_SIGNED_STATUS,
    IDS_TRANSFER_STATUS,    
    ID_SEPARATOR,
	IDS_TASKBAR
};

int CComposeFrame::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
   int retval = CGenericFrame::OnCreate ( lpCreateStruct );

	//I'm hardcoding string since I don't want it translated.
	m_pChrome->CreateCustomizableToolbar("Compose_Message", 3, TRUE);
	CButtonToolbarWindow *pWindow;
	BOOL bOpen, bShowing;

	int32 nPos;

	m_pChrome->LoadToolbarConfiguration(ID_COMPOSE_MESSAGE_TOOLBAR, CString("Message_Toolbar"),nPos, bOpen, bShowing);

	// Set up toolbar
	LPNSTOOLBAR pIToolBar = NULL;
	m_pChrome->QueryInterface(IID_INSToolBar,(LPVOID*)&pIToolBar);
	if ( pIToolBar ) 
   {
		pIToolBar->Create( this );
		pIToolBar->SetButtons( ComposeCodes, sizeof(ComposeCodes) / sizeof(UINT) );
		pIToolBar->SetSizes( CSize( 23+7, 21+6), CSize( 23, 21 ) );
	   // this is currently loading one bitmap.. this needs to be fixed to add
	   // the deferred bitmaps as well.
		pIToolBar->LoadBitmap(MAKEINTRESOURCE(IDB_COMPOSETOOLBAR));
		pIToolBar->SetToolbarStyle( theApp.m_pToolbarStyle );
		pWindow = new CButtonToolbarWindow(CWnd::FromHandlePermanent(pIToolBar->GetHWnd()), theApp.m_pToolbarStyle, 43, 27, eLARGE_HTAB);
		m_pChrome->GetCustomizableToolbar()->AddNewWindow(ID_COMPOSE_MESSAGE_TOOLBAR, pWindow,nPos, 50, 37, 0, CString(szLoadString(ID_COMPOSE_MESSAGE_TOOLBAR)),theApp.m_pToolbarStyle, bOpen, FALSE);
		m_pChrome->ShowToolbar(ID_COMPOSE_MESSAGE_TOOLBAR, bShowing);
		m_pChrome->GetCustomizableToolbar()->SetBottomBorder(TRUE);
		CWnd *pToolWnd = CWnd::FromHandlePermanent( pIToolBar->GetHWnd() );
		if ( pToolWnd ) {
            CStationaryToolbarButton * pStationaryToolbarButton = new CStationaryToolbarButton;
            pStationaryToolbarButton->Create(pToolWnd, theApp.m_pToolbarStyle, CSize(50, 40), CSize(30, 27), 
               szLoadString(IDS_TOOLATTACH),
               szLoadString(IDS_TOOLATTACHFILES),
               szLoadString(IDS_TOOLATTACHALL),
					IDB_ATTACHBUTTON, 0, CSize(23, 21), TRUE, IDM_ATTACHFILE, 10, TB_HAS_IMMEDIATE_MENU);
			pIToolBar->AddButton( pStationaryToolbarButton, 3 );
		}

      if (bSendDeferred)
      {
           pIToolBar->RemoveButton(0);
   		   CCommandToolbarButton *pCommandButton = new CCommandToolbarButton;
       	   CString statusStr, toolTipStr, textStr;
		   WFE_ParseButtonString(IDM_SENDLATER, statusStr, toolTipStr, textStr);
		   DWORD dwButtonStyle = 0;
		   pCommandButton->Create(pToolWnd, theApp.m_pToolbarStyle, CSize(44, 37), CSize(25, 25),
						(const char*) textStr,(const char*) toolTipStr, (const char*) statusStr,
						IDB_COMPOSETOOLBAR, 7, CSize(23,21), IDM_SENDLATER, -1, dwButtonStyle);
		   pIToolBar->AddButton(pCommandButton, 0);
		   CCommandToolbar *pToolBar = (CCommandToolbar *) CWnd::FromHandlePermanent( pIToolBar->GetHWnd() );
           pToolBar->ReplaceButtonBitmapIndex(IDM_SENDNOW,7);
      }

    	m_pChrome->Release();
	}

   // create the address/attachment widget dialog bar
   m_pComposeBar = new CComposeBar;
   ASSERT(m_pComposeBar);
   m_pComposeBar->Create ( this, (UINT) IDD_COMPOSEBAR, 
						   (UINT) WS_CLIPCHILDREN|CBRS_TOP, 
		                   (UINT) IDD_COMPOSEBAR );

   if (m_bUseHtml)
   {
      if (CreateHtmlToolbars() == -1)
         return -1;
        
      RecalcLayout();        
      // Check if format toolbar needs more room and 
      // increase window width to fit it, but observe 600 pixel maximum width
      int iWidth = min(600, m_pToolBarController->GetCharacterBar()->GetToolbarWidth()
                       + GetSystemMetrics(SM_CXFRAME) + 8); // Kludge - 8 more needed to work right!

      if( iWidth > lpCreateStruct->cx ){
         SetWindowPos(NULL, 0, 0, iWidth,
                      lpCreateStruct->cy, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
      }
   }
   else
   {
      CreatePlainTextEditor();
   }

   // create a unique title for the composition window    
   CString cs;
   cs = szLoadString(IDS_COMPOSITION);
   GetChrome()->SetWindowTitle(cs);

    // Instead of using ANSI_VAR_FONT for i18n
	SetCSID(INTL_DefaultWinCharSetID(0));

   // create the status bar
	m_barStatus.Create( this );
	m_barStatus.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));

	LPNSSTATUSBAR pIStatusBar = NULL;
	m_pChrome->QueryInterface( IID_INSStatusBar, (LPVOID *) &pIStatusBar );
	if ( pIStatusBar ) {
		pIStatusBar->Attach( &m_barStatus );
		pIStatusBar->Release();
	}

   if (m_bUseHtml)
      ShowHtmlToolbars();

    // Attach the submenus shared by Composer, Message Composer, and right-button popups
    HMENU hMenu = ::GetMenu(m_hWnd);
    if( hMenu )
    {
        HMENU hTableMenu = ::GetSubMenu(hMenu, ED_MENU_TABLE);
        if( hTableMenu )
        {
            ::ModifyMenu( hTableMenu, ID_SELECT_TABLE, MF_BYCOMMAND | MF_POPUP | MF_STRING,
                          (UINT)::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(IDM_COMPOSER_TABLE_SELECTMENU)),
                          szLoadString(IDS_SUBMENU_SELECT_TABLE) );
            ::ModifyMenu( hTableMenu, ID_INSERT_TABLE, MF_BYCOMMAND | MF_POPUP | MF_STRING,
                          (UINT)::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(IDM_COMPOSER_TABLE_INSERTMENU)),
                          szLoadString(IDS_SUBMENU_INSERT_TABLE) );
            ::ModifyMenu( hTableMenu, ID_DELETE_TABLE, MF_BYCOMMAND | MF_POPUP | MF_STRING,
                          (UINT)::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(IDM_COMPOSER_TABLE_DELETEMENU)),
                          szLoadString(IDS_SUBMENU_DELETE_TABLE) );
        }
    }
   return retval;
}

// OnCreateClient() sets up the context information and create the
// contained CView derived class.

BOOL CComposeFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext)
{
	 BOOL bRetval = CGenericFrame::OnCreateClient(lpcs, pContext);

    // Get the current view
    CWnd *pView = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
    ASSERT(pView);
	 CWinCX * pWinCX = new CWinCX(
	   (CGenericDoc*)pContext->m_pCurrentDoc, this, (CGenericView *)pView );

	 //      This is the main and active context.
	 SetMainContext(pWinCX);
	 SetActiveContext(pWinCX);
	RECT rect;
	GetClientRect(&rect);
    pWinCX->Initialize(FALSE, &rect);
    pWinCX->GetContext()->fancyFTP = TRUE;
    pWinCX->GetContext()->fancyNews = TRUE;
    pWinCX->GetContext()->intrupt = FALSE;
    if (m_bUseHtml)
	      pWinCX->GetContext()->is_editor = TRUE;

    pWinCX->GetContext()->reSize = FALSE;
    return bRetval;
}

// PreCreateWindow() sets the initial compose window size based on
// values that were saved in the registry during OnClose().  The width
// is currently hard-coded to open up to 73 (COMPOSE_WINDOW_WIDTH)
// characters wide.

BOOL CComposeFrame::PreCreateWindow ( CREATESTRUCT &cs )
{
	cs.style |= WS_CLIPCHILDREN;

    BOOL rv = CGenericFrame::PreCreateWindow ( cs );

	int16 iLeft,iRight,iTop,iBottom;
	PREF_GetRectPref("mail.compose_window_rect", &iLeft, &iTop, &iRight, &iBottom);
	// Restore X,Y and height, but recalculate width
    if ( iLeft != -1) 
    {
		cs.x = iLeft;
		cs.y = iTop;
		cs.cy = iBottom-iTop;
        if (m_bUseHtml)
        {
            cs.cx = iRight - iLeft;
            return rv;
        }
	} 

    CWnd *pCwnd = AfxGetMainWnd();
    ASSERT(pCwnd);
    
    // get the current character width from the font size, first
    // we have to create the font.
    CClientDC dc(pCwnd);
    HFONT font;
	CFont *pOldFont;
    LOGFONT lf;
    XP_MEMSET(&lf,0,sizeof(LOGFONT));
    TEXTMETRIC tm;

    lf.lfPitchAndFamily = FF_MODERN | FIXED_PITCH;
    strcpy(lf.lfFaceName, IntlGetUIFixFaceName(0));
    lf.lfHeight = -MulDiv(9,dc.GetDeviceCaps(LOGPIXELSY), 72);
    lf.lfQuality = PROOF_QUALITY;    
	font = theApp.CreateAppFont( lf );

    pOldFont = dc.SelectObject(CFont::FromHandle(font));
    dc.GetTextMetrics(&tm);
    dc.SelectObject(pOldFont);

    cs.cx = (GetSystemMetrics(SM_CXFRAME)*2) + 
        (tm.tmAveCharWidth*(COMPOSE_WINDOW_WIDTH+(m_bUseHtml?2:0)));
    cs.cx += GetSystemMetrics(SM_CXVSCROLL);
    cs.cx += EDIT_MARGIN_OFFSET;
    
    // use 2/3 of the screen as the standard default height
    cs.cy = (GetSystemMetrics(SM_CYSCREEN)/3)*2;
	theApp.ReleaseAppFont(font);
    return rv;
}

void CComposeFrame::DisplayHeaders ( MSG_HEADER_SET headers )
{
   ASSERT(m_pComposeBar);
   m_pComposeBar->DisplayHeaders ( headers );
	m_pComposeBar->UpdateFixedSize ( );
   RecalcLayout ( );
#ifdef XP_WIN16
	m_pComposeBar->Invalidate();
#endif
   m_SavedHeaders = headers;
}

LONG CComposeFrame::OnToolController(UINT,LONG)
{
    if (m_bUseHtml)
	return (LONG)m_pToolBarController;
	return 0;
}

void CComposeFrame::SetModified(BOOL bValue)
{
    if (m_bUseHtml)
        EDT_SetDirtyFlag(GetMainContext()->GetContext(), bValue);
    else
        m_EditorParent.SetModified(bValue);
}

void CComposeFrame::OnSetFocus(CWnd * pOldWnd) 
{
    CGenericFrame::OnSetFocus(pOldWnd);
    if (GetFocusField())
        GetFocusField()->SetFocus();
}

BOOL CComposeFrame::CanCloseFrame(void)
{
    BOOL bModified = FALSE;
    if (m_bUseHtml){
        bModified = EDT_DirtyFlag(GetMainContext()->GetContext());
        MWContext *pMWContext;
        if( GetMainContext() == NULL ||
            (pMWContext = GetMainContext()->GetContext()) == NULL )
            return(FALSE);

        // Ignore close if in the process of doing something
        if( pMWContext->edit_saving_url ||
            pMWContext->waitingMode ||
            EDT_IsBlocked(pMWContext)  )
            return(FALSE);
    } else
        bModified = m_EditorParent.m_bModified;

	if (bModified) 
	{
	    CString csText, csTitle;
	    csText.LoadString(IDS_MESSAGENOTSENT);
	    csTitle.LoadString(IDS_MESSAGECOMPOSITION);
	    if(MessageBox( csText, csTitle, MB_YESNO | MB_ICONEXCLAMATION)==IDNO)
    		 return(FALSE);
	}
    return(TRUE);
    
}

void CComposeFrame::OnClose ( void )
{
	// See if the document will allow closing.
    
 	if (!CanCloseFrame())
		return;
    
    m_pComposeBar->Cleanup();
    
    if (m_bUseHtml){
        MWContext *pMWContext;
        if( GetMainContext() == NULL ||
            (pMWContext = GetMainContext()->GetContext()) == NULL )
            return;

        // Stop any active plugin
        if (!CheckAndCloseEditorPlugin(pMWContext)) 
            return;
        EDT_DestroyEditBuffer(pMWContext);
    }

    // Save the window position... as long as it is not iconified or
    // maximized... 
   if ( !IsIconic() && !IsZoomed() )
   {
      CRect rect;
	   GetWindowRect ( &rect );
	   PREF_SetRectPref("mail.compose_window_rect", 
         (int16)rect.left, (int16)rect.top, (int16)rect.right, (int16)rect.bottom);
	}
	    
   CWinCX * pWinCX = (CWinCX*)GetMainContext();
   if ( pWinCX )
   {
	   // close the context and stuff
	   CGenericView * pView = pWinCX->GetView();
	   if ( pView ) 
	      pView->FrameClosing ( );
   }
  
	m_pChrome->SaveToolbarConfiguration(ID_COMPOSE_MESSAGE_TOOLBAR, CString("Message_Toolbar"));
#ifdef XP_WIN32
    // Save toolbar state
   CFrameWnd::SaveBarState(ToolBarProfileName);
#endif
   CGenericFrame::OnClose();
}

// Used by the template to set the context type.

void CComposeFrame::SetType(MWContextType type)
{
    MWContext * xpContext = GetMainContext()->GetContext();
    ASSERT(xpContext);
    xpContext->type = type;
}

#ifdef XP_WIN32
int16 CComposeFrame::GetTitleWinCSID()	// jliu - used by genframe.cpp
{
	return INTL_DocToWinCharSetID( m_iCSID );
}
#endif

void CComposeFrame::SetCSID(int16 iCSID)
{
   if(iCSID != m_iCSID)
   {	
	m_iCSID = iCSID;
    m_pComposeBar->SetCSID(iCSID);
	if (m_cfTextFont) {
		theApp.ReleaseAppFont(m_cfTextFont);
	}

	CClientDC dc(this);
	LOGFONT lf;                 
	XP_MEMSET(&lf,0,sizeof(LOGFONT));
	lf.lfPitchAndFamily = FF_MODERN | FIXED_PITCH;
	strcpy(lf.lfFaceName, IntlGetUIFixFaceName(m_iCSID));
	lf.lfCharSet = IntlGetLfCharset(m_iCSID); 
	lf.lfHeight = -MulDiv(9,dc.GetDeviceCaps(LOGPIXELSY), 72);
	lf.lfQuality = PROOF_QUALITY;    
	m_cfTextFont = theApp.CreateAppFont( lf );
    TEXTMETRIC tm;
    CFont * pOld = dc.SelectObject(CFont::FromHandle(m_cfTextFont));
    dc.GetTextMetrics(&tm);
    dc.SelectObject(pOld);
    m_cxChar = tm.tmAveCharWidth;

	if (!m_bUseHtml)
		::SendMessage(m_Editor.GetSafeHwnd(), WM_SETFONT, (WPARAM)m_cfTextFont, FALSE);
	else
		EDT_SetEncoding(GetMainContext()->GetContext(), m_iCSID);

#ifdef XP_WIN32
	// jliu added the following to support CJK caption print
	SendMessage( WM_SETTINGCHANGE );
#endif

	RecalcLayout();
   }
}

// OnUpdateThis() enables/disables UI menus/tools based on the state
// which the backend is reporting.  If the menu is a check-item, that
// will be reflected into the appropriate item as well.

void CComposeFrame::OnUpdateThis ( CCmdUI* pCmdUI, MSG_CommandType tMenuType )
{
   if (!m_pComposePane || !pCmdUI)
      return;

	if (GetMsgPane() && MSG_DeliveryInProgress(GetMsgPane())) 
   {
      pCmdUI->Enable(FALSE);
      return;
   }

   XP_Bool bSelectable = FALSE;                    // is this a selectable command?
   XP_Bool bPlural = FALSE;                        // indicates whether to append "s" (which we never do)
   MSG_COMMAND_CHECK_STATE sState = MSG_Unchecked; // checked or unchecked 

    // call the backend requesting various state information
	MSG_CommandStatus( 
	m_pComposePane, // message pane
		tMenuType,      // MSG_CommandType value
		NULL,           
	0,
		&bSelectable,   // enable/disable
		&sState,        // checked/unchecked
		NULL,
		&bPlural);      // plural command?

    pCmdUI->Enable(bSelectable);                // enable or disable item
    pCmdUI->SetCheck(sState == MSG_Checked);    // set the check state

}

void CComposeFrame::MessageCommand(MSG_CommandType msgCmd)
{
    ASSERT(m_pComposePane);
    MSG_Command(m_pComposePane, msgCmd, NULL, 0);
}

void CComposeFrame::OnWrapLongLines ( )
{
   if (!m_bUseHtml)
   {
      int bHasFocus = 0;
      m_bWrapLongLines ^= 1;
      CRect rect;
      CString cs;

      if (GetFocus() == (CWnd *)&m_Editor)
          bHasFocus = 1;

      m_Editor.GetWindowRect(rect);
      m_EditorParent.ScreenToClient(rect);
      m_Editor.GetWindowText(cs);
      CFont *pFont = m_Editor.GetFont();

      m_Editor.DestroyWindow();
      m_Editor.Create ( 
          WS_VSCROLL | ES_MULTILINE | WS_CHILD | WS_VISIBLE | 
              ES_AUTOVSCROLL | ES_WANTRETURN | (m_bWrapLongLines ? 0 : ES_AUTOHSCROLL|WS_HSCROLL),
          rect, &m_EditorParent, IDC_COMPOSEEDITOR);
      
	  m_Editor.SetFont(pFont);
      m_Editor.SetWindowText(cs);

      if (bHasFocus)
          m_Editor.SetFocus();
   } 
}

void CComposeFrame::OnUpdateWrapLongLines ( CCmdUI * pCmdUI )
{
   OnUpdateButtonGeneral(pCmdUI);
   pCmdUI->SetCheck(m_bWrapLongLines == 1);
}

void CComposeFrame::OnNew ( )
{
    MSG_Command(GetMsgPane(), MSG_MailNew, NULL, 0);
}

void CComposeFrame::OnSelectAll()
{
    if (m_bUseHtml)
    	EDT_SelectAll(GetMainContext()->GetContext());
    else
    {
    	m_Editor.SetSel(0,-1);
    	m_Editor.SetFocus();
    }
}

void CComposeFrame::OnUpdateSelectAll(CCmdUI *pCmdUI)
{
    pCmdUI->Enable(TRUE);
}

void CComposeFrame::OnUpdatePasteQuote(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(
		IsClipboardFormatAvailable(CF_TEXT)
#ifdef XP_WIN32
			|| IsClipboardFormatAvailable(CF_UNICODETEXT)
#endif
	);
}

void CComposeFrame::OnPasteQuote( )
{       
    char *lpszText;
    HANDLE hszText;
    char *lpszQuotedText = NULL;
    OpenClipboard();

#ifdef XP_WIN32	// ftang
	BOOL bDoneByUnicode = FALSE;
	// Try to use CF_UNICODETEXT first
    if ((CS_USER_DEFINED_ENCODING != (m_iCSID & ~CS_AUTO)) && 
		IsClipboardFormatAvailable(CF_UNICODETEXT) &&
		(hszText = (HANDLE) GetClipboardData(CF_UNICODETEXT)) )
    {
		char* lpszConvertedText = NULL;
    	lpszText = (char *) GlobalLock(hszText);

		// Now, let's convert the Unicode text into the datacsid encoding
		int ucs2len = CASTINT(INTL_UnicodeLen((INTL_Unicode*)lpszText));
		int	mbbufneeded = CASTINT(INTL_UnicodeToStrLen((m_iCSID & ~CS_AUTO), 
														(INTL_Unicode*)lpszText, 
														ucs2len));
		if(NULL != (lpszConvertedText = (char*)XP_ALLOC(mbbufneeded + 1)))
		{
			INTL_UnicodeToStr(m_iCSID, (INTL_Unicode*)lpszText, ucs2len, 
										(unsigned char*) lpszConvertedText, mbbufneeded + 1);
			
			if (m_bUseHtml) {
				MSG_PastePlaintextQuotation(GetMsgPane(), lpszConvertedText);
			} else {
				lpszQuotedText = MSG_ConvertToQuotation(lpszConvertedText);
				FE_InsertMessageCompositionText(GetMainContext()->GetContext(),
												lpszQuotedText, FALSE);
			}
			XP_FREEIF(lpszConvertedText);

			bDoneByUnicode = TRUE;
		}
    	GlobalUnlock(hszText);
    }
	
	if(bDoneByUnicode)
	{
		// Already done. Do nothing
	}
	else // not done by unicode try CF_TEXT now
#endif
    if (hszText = (HANDLE) GetClipboardData(CF_TEXT)) 
    {
    	lpszText = (char *) GlobalLock(hszText);
		if (m_bUseHtml) {
			MSG_PastePlaintextQuotation(GetMsgPane(), lpszText);
		} else {
			lpszQuotedText = MSG_ConvertToQuotation(lpszText);
			FE_InsertMessageCompositionText(GetMainContext()->GetContext(),
											lpszQuotedText, FALSE);
		}
    	GlobalUnlock(hszText);
    }
	if (lpszQuotedText) XP_FREE(lpszQuotedText);
    CloseClipboard();
}

// UpdateToolbar() changes the command IDs as well as the assigned bitmap
// to reflect the current state.. deferred send or immediate delivery.
void CComposeFrame::UpdateToolBar(void)
{
    ApiToolBar(pIToolBar,m_pChrome);

	if ( pIToolBar ) 
    {
        pIToolBar->SetButtons(ComposeCodes, sizeof(ComposeCodes) / sizeof(UINT));
        // this is currently loading one bitmap.. this needs to be fixed to add
        // the deferred bitmaps as well.
        pIToolBar->LoadBitmap(MAKEINTRESOURCE(IDB_COMPOSETOOLBAR));
	}
}

void CComposeFrame::OnDoneGoingOffline()
{
    BOOL bGlobalDeferred = FALSE;
	PREF_GetBoolPref("network.online", &bGlobalDeferred);
	bSendDeferred = !bGlobalDeferred;
  	LPNSTOOLBAR pIToolBar;
	m_pChrome->QueryInterface( IID_INSToolBar, (LPVOID *) &pIToolBar );

	if ( pIToolBar ) 
    {
        CString statusStr, toolTipStr, textStr;
        if (bSendDeferred)
        {
            WFE_ParseButtonString(IDM_SENDLATER, statusStr, toolTipStr, textStr);
		    CCommandToolbar *pToolBar = (CCommandToolbar *) CWnd::FromHandlePermanent( pIToolBar->GetHWnd() );
            if ( pToolBar)
            {
                CToolbarButton *pBarButton = pToolBar->GetNthButton(0);
                if (pBarButton)
                {
 		            pToolBar->ReplaceButtonBitmapIndex(IDM_SENDNOW, 7);
                    pBarButton->SetToolTipText(toolTipStr);
                    pBarButton->SetStatusText(statusStr);
                    pBarButton->SetText(textStr);
                    pBarButton->SetButtonCommand(IDM_SENDLATER);
                }
		        pIToolBar->Release();
            }
        }
        else
        {
		    WFE_ParseButtonString(IDM_SENDNOW, statusStr, toolTipStr, textStr);
		    CCommandToolbar *pToolBar = (CCommandToolbar *) CWnd::FromHandlePermanent( pIToolBar->GetHWnd() );
            if (pToolBar)
            {
                CToolbarButton *pBarButton = pToolBar->GetNthButton(0);
 	            pToolBar->ReplaceButtonBitmapIndex(IDM_SENDLATER, 0);
                if (pBarButton)
                {
                    pBarButton->SetToolTipText(toolTipStr);
                    pBarButton->SetStatusText(statusStr);
                    pBarButton->SetText(textStr);
                    pBarButton->SetButtonCommand(IDM_SENDNOW);
                }
                pIToolBar->Release();
            }
        }
    }
}

void CComposeFrame::OnUpdateButtonGeneral(CCmdUI *pCmdUI)    
{ 
	OnUpdateThis(pCmdUI, MSG_SendMessage );
}

void CComposeFrame::OnUpdateSend(CCmdUI *pCmdUI)
{
	OnUpdateThis(pCmdUI, bSendDeferred ? MSG_SendMessageLater : MSG_SendMessage);
	pCmdUI->SetText(bSendDeferred ? szLoadString(IDS_DEFAULTSENDNOW) : szLoadString(IDS_DEFAULTSENDLATER));
}

void CComposeFrame::OnUpdateSendNow(CCmdUI *pCmdUI)
{

	OnUpdateThis(pCmdUI, MSG_SendMessage);
	pCmdUI->SetText(bSendDeferred ? szLoadString(IDS_SENDNOW) : szLoadString(IDS_DEFAULTSENDNOW));
}

void CComposeFrame::OnUpdateSendLater(CCmdUI *pCmdUI)
{

	OnUpdateThis(pCmdUI, MSG_SendMessageLater);
	pCmdUI->SetText(bSendDeferred ? szLoadString(IDS_DEFAULTSENDLATER) : szLoadString(IDS_SENDLATER));
}

// This function is send to the backend for a callback when quoting
// text. Currently, it simply calls the pre-existing insert code.
int WFE_InsertMessage(void *closure, const char * data)
{
   if (data) {
      FE_InsertMessageCompositionText((MWContext*)closure,data,FALSE);
   }
   else {
	   MSG_Pane *pPane = MSG_FindPane((MWContext *)closure, MSG_COMPOSITIONPANE);
	   if (pPane) {
			CComposeFrame * pCompose = (CComposeFrame *) MSG_GetFEData(pPane);
			if (pCompose->GetQuoteSel() != -1) {
				if (! pCompose->UseHtml()) {
					int32 eReplyOnTop = 0;
					int start = ((int) pCompose->GetQuoteSel()), end = start;
					int tmpStart = 0;

					if (PREF_NOERROR ==
							PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop)) {
						switch(eReplyOnTop) {
						case 1:
						default:
							pCompose->GetEditor()->SetSel(start, end);
							break;
						case 2:
							pCompose->GetEditor()->GetSel(tmpStart, end);
							pCompose->GetEditor()->SetSel(start, end);
							break;
						case 3:
							pCompose->GetEditor()->GetSel(tmpStart, end);
							pCompose->GetEditor()->SetSel(end, start);
							break;
						}
					}
				}
				pCompose->SetQuoteSel(-1);
			}
	   }
   }
   return 0;
}

void CComposeFrame::OnQuoteOriginal( void )
{
	SetQuoteSelection();
    MSG_QuoteMessage(GetMsgPane(),WFE_InsertMessage,(void*)GetMainContext()->GetContext());
}

void CComposeFrame::OnAddressTab(void)
{
    m_pComposeBar->OnAddressTab();
}

void CComposeFrame::OnAttachTab(void)
{
    m_pComposeBar->OnAttachTab();
}

void CComposeFrame::OnOptionsTab(void)
{
    m_pComposeBar->OnOptionsTab();
}

void CComposeFrame::OnCollapse(void)
{
    m_pComposeBar->OnCollapse();
}

// OnSaveAs().. This should probably have better backend support.  This function
// gets the current message body text and saves it to a user specified location.

void CComposeFrame::OnSaveAs(void)
{
    // Call the file-picker to get a filename/location.
    char * pPath = wfe_GetSaveFileName(m_hWnd, XP_GetString(MK_MSG_SAVE_AS), szLoadString(IDS_DEFAULTSAVEAS), 0);
    
    // if a valid path was specified, continue with save
	if (pPath && strlen(pPath)) 
    {
		uint32 ulBodySize;
		char *pBody;
	
	// open the file for writing
		FILE *pFile = fopen(pPath, "w");
	
	// if the file was opened successfully, continue with save
		if (pFile)
	{
			FE_GetMessageBody(m_pComposePane, &pBody, &ulBodySize, NULL);
			if (!fwrite(pBody, sizeof(char), CASTSIZE_T(ulBodySize), pFile)) 
	    {
		// couldn't write file
				char szOpenError[512];
				sprintf(szOpenError, szLoadString(IDS_CANTWRITETOFILE), pPath);
				FE_Alert(GetMainContext()->GetContext(), szOpenError);
			}
			if (UseHtml())
				XP_HUGE_FREE(pBody);
			else
				XP_FREE(pBody); 
			fclose(pFile);
		} 
	else 
	{
	    // can't open file
			char szOpenError[512];
			sprintf(szOpenError, szLoadString(IDS_CANTWRITETOFILE), pPath);
			FE_Alert(GetMainContext()->GetContext(), szOpenError);
		}
		XP_FREE(pPath);
	}
}


void CComposeFrame::OnCheckSpelling()
{
   CWnd * pWnd = GetEditorWnd();
   if (pWnd)
      pWnd->SendMessage(WM_COMMAND,ID_CHECK_SPELLING);
}

// OnSaveAsDraft() sends based on the current delivery state.

void CComposeFrame::OnSaveDraft ( void )

{
    ASSERT(m_pComposeBar);
    m_pComposeBar->UpdateHeaderInfo ( );

    // first we must get the text from the front end and then
    // tell the backend what text to send.

	uint32 ulBodySize;
	char *pBody;

	FE_GetMessageBody(GetMsgPane(), &pBody, &ulBodySize, NULL);
    
    // if we could get text, tell the backend
    if (ulBodySize) 
	MSG_SetCompBody(GetMsgPane(),pBody);

    // start the meteors tumbling    
	GetChrome()->StartAnimation();

	MWContext *context;
	context = GetMainContext()->GetContext();

    // Pass current csid to backend, so it can do right conversion for 
    // 8bit header.    
	INTL_SetCSIDocCSID(LO_GetDocumentCharacterSetInfo(context), m_iCSID);

    MSG_Command(GetMsgPane(), MSG_SaveDraft, 0, 0);
	
	GetChrome()->StopAnimation();

    if (!m_bUseHtml)
        m_EditorParent.m_bModified = FALSE;
}



void CComposeFrame::OnUpdateSaveDraft(CCmdUI *pCmdUI)
{

    // Change the string based on the deferred sending status

    OnUpdateThis(pCmdUI, MSG_SaveDraft );

}


void CComposeFrame::OnSaveTemplate ( void )

{
    ASSERT(m_pComposeBar);
    m_pComposeBar->UpdateHeaderInfo ( );

    // first we must get the text from the front end and then
    // tell the backend what text to send.

	uint32 ulBodySize;
	char *pBody;

	FE_GetMessageBody(GetMsgPane(), &pBody, &ulBodySize, NULL);
    
    // if we could get text, tell the backend
    if (ulBodySize) 
	MSG_SetCompBody(GetMsgPane(),pBody);

    // start the meteors tumbling    
	GetChrome()->StartAnimation();

	MWContext *context;
	context = GetMainContext()->GetContext();

    // Pass current csid to backend, so it can do right conversion for 
    // 8bit header.    
	INTL_SetCSIDocCSID(LO_GetDocumentCharacterSetInfo(context), m_iCSID);

    MSG_Command(GetMsgPane(), MSG_SaveTemplate, 0, 0);
	
	GetChrome()->StopAnimation();

    if (!m_bUseHtml)
        m_EditorParent.m_bModified = FALSE;
}

void CComposeFrame::OnUpdateSaveTemplate(CCmdUI *pCmdUI)
{

    // Change the string based on the deferred sending status

    OnUpdateThis(pCmdUI, MSG_SaveTemplate );

}


LRESULT CComposeFrame::OnButtonMenuOpen(WPARAM wParam, LPARAM lParam)
{
	HMENU hMenu = (HMENU) lParam;
	UINT nCommand = (UINT) LOWORD(wParam);
    if (nCommand == IDM_ATTACHFILE){
        ::AppendMenu(hMenu,MF_STRING|MF_ENABLED, IDM_ATTACHFILE, szLoadString(IDS_TOOLATTACHFILE));
        ::AppendMenu(hMenu,MF_STRING|MF_ENABLED, IDM_ATTACHWEBPAGE, szLoadString(IDS_ATTACHWEBPAGE));
        ::AppendMenu(hMenu,MF_SEPARATOR, 0, NULL);
        ::AppendMenu(hMenu,MF_STRING|MF_ENABLED, IDM_ATTACHMYCARD, szLoadString(IDS_ATTACHMYCARD));
	    return 1;
    } else if (GetMainWinContext()){
        // Route other messages to Edit View, which handles messages common to all Composer frames
        return ::SendMessage( GetMainWinContext()->GetPane(), NSBUTTONMENUOPEN, wParam, lParam);
    }
    return 0;
}

// DoSend() sends based on the current delivery state.
void CComposeFrame::DoSend ( BOOL bNow )
{
    ASSERT(m_pComposeBar);
    LPADDRESSCONTROL pIAddress = GetAddressWidgetInterface();
    if (pIAddress)
    {
        CListBox * pListBox = pIAddress->GetListBox();
        if (::IsWindow(pListBox->m_hWnd))
	        pListBox->SendMessage(WM_NOTIFYSELECTIONCHANGE);
    }
	MSG_ClearComposeHeaders(GetMsgPane());
    m_pComposeBar->UpdateHeaderInfo ( );

    // first we must get the text from the front end and then
    // tell the backend what text to send.
	uint32 ulBodySize;
	char *pBody = NULL;
	FE_GetMessageBody(GetMsgPane(), &pBody, &ulBodySize, NULL);
    
#ifndef _WIN32
	if (ulBodySize > 32767) {
		MessageBox(szLoadString(IDS_MSGTOBIG), szLoadString(IDS_TITLE_ERROR), MB_ICONSTOP|MB_OK);
		return;
	}
#endif
    // if we could get text, tell the backend
    if (pBody) 
    	MSG_SetCompBody(GetMsgPane(),pBody);

    int errcode = 0;
    MWContext * context = GetMainContext()->GetContext();
    while ((errcode = MSG_SanityCheck(GetMsgPane(),errcode))!=0)
    {
    	GetChrome()->StopAnimation();
        if (errcode == MK_MSG_MISSING_SUBJECT)
        {
            char * ptr = PromptMessageSubject();
            if (ptr != NULL)
            {
    	        MSG_SetCompHeader(GetMsgPane(),MSG_SUBJECT_HEADER_MASK,ptr);
                XP_FREE(ptr);
            }
            else
                return;
        }
        else if (!FE_Confirm(context,XP_GetString(errcode)))
            return;
    }
    
    // Pass current csid to backend, so it can do right conversion for 
    // 8bit header.  
	INTL_SetCSIWinCSID(LO_GetDocumentCharacterSetInfo(context), m_iCSID);
   
    MSG_Command(GetMsgPane(), bNow ? MSG_SendMessage : MSG_SendMessageLater, 0, 0);

	if (GetMsgPane() && MSG_DeliveryInProgress(GetMsgPane())) {
		// start the meteors tumbling    
		GetChrome()->StartAnimation();
		LPNSSTATUSBAR pIStatusBar = NULL;
		GetChrome()->QueryInterface( IID_INSStatusBar, (LPVOID *) &pIStatusBar );
		if ( pIStatusBar ) {
			pIStatusBar->ModalStatus( TRUE, 0, szLoadString(IDS_SENDINGMESSAGE));
			pIStatusBar->Release();
		}
	}
  // rhp - this condition is for MAPI UI-less send operations...
  else
  {
    if (!IsWindowVisible())       // First, check if the window is not already visible
      ShowWindow(SW_SHOW);        // and if not, get it on the screen for the user to deal with...    
  }
  // rhp 
}

void CComposeFrame::OnAttachMyCard()
{
    ASSERT(m_pComposeBar);
    m_pComposeBar->SetAttachMyCard(!m_pComposeBar->GetAttachMyCard());
}

void CComposeFrame::OnUpdateAttachMyCard(CCmdUI * pCmdUI)
{
    pCmdUI->Enable(TRUE);
    pCmdUI->SetCheck(m_pComposeBar->GetAttachMyCard());
}

void CComposeFrame::OnAttachFile()
{
    ASSERT(m_pComposeBar);
    m_pComposeBar->OnAttachTab();
    m_pComposeBar->UpdateWindow();
    m_pComposeBar->AttachFile();
}
 
void CComposeFrame::OnAttachUrl()
{
    ASSERT(m_pComposeBar);
    m_pComposeBar->OnAttachTab();
    m_pComposeBar->UpdateWindow();
    m_pComposeBar->AttachUrl();
}

void CComposeFrame::ConvertToPlainText()
{
   if (MessageBox(szLoadString(IDS_CONVERTWARN), szLoadString(IDS_CONVERTTITLE), 
      MB_ICONEXCLAMATION | MB_YESNO) == IDYES)
   {

      MWContext *pMWContext;
      if( GetMainContext() == NULL ||
         (pMWContext = GetMainContext()->GetContext()) == NULL )
             return;
        // Stop any active plugin
      if (!CheckAndCloseEditorPlugin(pMWContext)) 
         return;
      EDT_DestroyEditBuffer(pMWContext);
      pMWContext->is_editor = FALSE;
      m_pChrome->SetMenu(IDR_COMPOSEPLAIN);
      DestroyHtmlToolbars();
      RecalcLayout();
      CreatePlainTextEditor();
	  ::SendMessage(m_Editor.GetSafeHwnd(), WM_SETFONT, (WPARAM)m_cfTextFont, FALSE);
      m_bUseHtml = FALSE;
   }
}

void CComposeFrame::ConvertToHtml()
{
   m_pChrome->SetMenu(IDR_COMPOSEFRAME);
   CreateHtmlToolbars();
   ShowHtmlToolbars();
   RecalcLayout();
   SetEditorParent(NULL);
   m_Editor.DestroyWindow();
   m_EditorParent.DestroyWindow();
   m_bUseHtml = TRUE;

   if (GetMainContext() != NULL)
   {
      CWinCX * pWinCX = GetMainContext()->IsFrameContext() ? 
         VOID2CX(GetMainContext(),CWinCX) : NULL;
      if (pWinCX != NULL)
      {
         MWContext * pContext = pWinCX->GetContext();
         if (!pContext->is_editor)
         {
            pContext->is_editor = TRUE;
            URL_Struct * pUrl = NET_CreateURLStruct(EDT_NEW_DOC_URL,NET_DONT_RELOAD);
            if (pUrl != NULL)
            {
               pWinCX->GetUrl(pUrl, FO_CACHE_AND_PRESENT);
               pWinCX->GetContext()->bIsComposeWindow = TRUE;
            }
         }  
      }
   }
}

void CComposeFrame::OnConvert()
{
   if (m_bUseHtml)   
      ConvertToPlainText();
   else
      ConvertToHtml();   
}

void CComposeFrame::OnUpdateConvert(CCmdUI * pCmdUI)
{
   pCmdUI->Enable(FALSE);   // wait for it
}

void CComposeFrame::OnSend()
{
	DoSend(!bSendDeferred);
}

void CComposeFrame::OnSendNow()
{
   DoSend(TRUE );
}

void CComposeFrame::OnSendLater()
{
   DoSend(FALSE);
}

void CComposeFrame::MakeComposeBarVisible()
{
    if (!GetComposeBar()->IsVisible())
		GetComposeBar()->OnToggleShow();
	else if (GetComposeBar()->IsCollapsed())
	{
		GetComposeBar()->OnCollapse();
        CCustToolbar * pToolbar = GetChrome()->GetCustomizableToolbar();
        if( pToolbar ) 
            pToolbar->RemoveExternalTab(1);
       	RecalcLayout();
	}
}

void CComposeFrame::OnViewAddresses()
{
	MakeComposeBarVisible();
    GetComposeBar()->SendMessage(WM_COMMAND,IDC_ADDRESSTAB);
}

void CComposeFrame::OnViewAttachments()
{
	MakeComposeBarVisible();
    m_pComposeBar->m_pSubjectEdit->SetFocus();
    UpdateWindow();
    GetComposeBar()->SendMessage(WM_COMMAND,IDC_ATTACHTAB);
}

void CComposeFrame::OnViewOptions()
{
	MakeComposeBarVisible();
    m_pComposeBar->m_pSubjectEdit->SetFocus();
    UpdateWindow();
    GetComposeBar()->PostMessage(WM_COMMAND,IDC_OPTIONSTAB);
}

void CComposeFrame::OnUpdateViewAddresses(CCmdUI * pCmdUI)
{
    pCmdUI->SetRadio(GetComposeBar()->GetTab() == IDX_COMPOSEADDRESS);
    pCmdUI->Enable(TRUE);
}

void CComposeFrame::OnUpdateViewAttachments(CCmdUI * pCmdUI)
{
    pCmdUI->SetRadio(GetComposeBar()->GetTab() == IDX_COMPOSEATTACH);
    pCmdUI->Enable(TRUE);
}

void CComposeFrame::OnUpdateViewOptions(CCmdUI * pCmdUI)
{
    pCmdUI->SetRadio(GetComposeBar()->GetTab() == IDX_COMPOSEOPTIONS);
    pCmdUI->Enable(TRUE);
}

// OnSetInitialFocus - Invoked after the frame window, toolbar and view are created.
// It sets the initial keyboard focus.

LONG CComposeFrame::OnSetInitialFocus(WPARAM wParam, LPARAM lParam)
{
    return 0;
}

void CComposeFrame::SetInitialText( const char *pText )
{
	if (m_pInitialText)
		XP_FREE (m_pInitialText);

	m_pInitialText = NULL;

	if (pText)
		m_pInitialText = XP_STRDUP(pText);
}

BOOL CComposeFrame::BccOnly(void)
{
    LPADDRESSCONTROL pIAddress = GetAddressWidgetInterface();
    CListBox * pListBox = pIAddress->GetListBox();
    int count = pListBox->GetCount();
    int total = 0;
    for (int i=0; i<count; i++)
    {
        char * szName, * szType;
	    if(pIAddress->GetEntry(i,&szType, &szName))
		    if(strcmp(szType,szLoadString(IDS_ADDRESSBCC)))
			    total++;
    }
    return !total;
}

void CComposeFrame::InsertInitialText(void)
{
	const char *pBody = MSG_GetCompBody(GetMsgPane());

    LPADDRESSCONTROL pIAddress = GetAddressWidgetInterface();
    if (pIAddress)
    {
        CListBox * pListBox = pIAddress->GetListBox();
        if (::IsWindow(pListBox->m_hWnd))
    		pListBox->ResetContent();
    }

	if (m_pInitialText && strlen(m_pInitialText))
	{
		EDT_ReadFromBuffer(GetMainContext()->GetContext(),
						m_pInitialText);
		XP_FREEIF(m_pInitialText);
	}
	else if (pBody && strlen(pBody))
	{
		FE_InsertMessageCompositionText( GetMainContext()->GetContext(), pBody, TRUE);
		FE_InsertMessageCompositionText( GetMainContext()->GetContext(), "\n", TRUE);
		MSG_SetCompBody(GetMsgPane(), "");
	}

	CompleteComposeInitialization();
    if (m_bUseHtml)
        EDT_SetDirtyFlag(GetMainContext()->GetContext(), FALSE);

    if (MSG_ShouldAutoQuote(GetMsgPane()))
    {
		SetQuoteSelection();
        MSG_QuoteMessage(GetMsgPane(),WFE_InsertMessage,(void*)GetMainContext()->GetContext());
    }
}

void CComposeFrame::CompleteComposeInitialization(void)
{
    MSG_HeaderEntry * entry;
	int nCount;
    nCount = MSG_RetrieveStandardHeaders(GetMsgPane(),&entry);
    LPADDRESSCONTROL pIAddress = m_pComposeBar->GetAddressWidgetInterface();
    CListBox * pListBox = pIAddress->GetListBox();
	if (nCount > 0) 
	{
		int i;
        pListBox->LockWindowUpdate();
		for (i=0; i<nCount; i++)
		{
            pIAddress->AppendEntry(FALSE,
	    	    MSG_HeaderMaskToString(entry[i].header_type),
                entry[i].header_value,
                0, 0);
			XP_FREE(entry[i].header_value);
		}
        pIAddress->AppendEntry();
        ::LockWindowUpdate(NULL);
	}

	if (entry)
		XP_FREE(entry);

	const char * subject = MSG_GetCompHeader(GetMsgPane(),MSG_SUBJECT_HEADER_MASK);

    if (!nCount)
    	pIAddress->AppendEntry(NULL);

    // in case of the existance hidden headers, get new count
    nCount = pListBox->GetCount();

    if (subject && strlen(subject))
    {
    	m_pComposeBar->m_pSubjectEdit->SetWindowText(subject);
        GetChrome()->SetDocumentTitle(subject);
    }

	if (nCount<=1 || BccOnly())  
    {
        if (BccOnly())
        {
            pIAddress->DeleteEntry(pListBox->GetCount()-1);
        	pIAddress->AppendEntry(TRUE, szLoadString(IDS_ADDRESSTO),"",0,0);
        }
        pListBox->UpdateWindow();
		pListBox->SetFocus();
        SetFocusField(pListBox);
    }
	else if (subject && strlen(subject))
    {
    	if (!UseHtml())
        {
	    	GetEditor()->SetFocus();
            SetFocusField(GetEditor());
        }
    }
	else
	{
		m_pComposeBar->m_pSubjectEdit->SetSel(-1,0);
		m_pComposeBar->m_pSubjectEdit->SetFocus();
        SetFocusField(m_pComposeBar->m_pSubjectEdit);
	}

   // Set the message priority based on the fileds
    const char * priorityString = MSG_GetCompHeader(GetMsgPane(), MSG_PRIORITY_HEADER_MASK);
	if (priorityString) 
	{
		// Backend deals only untranslated priority; we have to do some mapping here
		if (strstr(priorityString, "Highest"))
			m_pComposeBar->m_iPriorityIdx = MSG_HighestPriority - 2;
		else if (strstr(priorityString, "High"))
			m_pComposeBar->m_iPriorityIdx = MSG_HighPriority - 2;
		else if (strstr(priorityString, "Lowest"))
			m_pComposeBar->m_iPriorityIdx = MSG_LowestPriority - 2;
		else if (strstr(priorityString, "Low"))
			m_pComposeBar->m_iPriorityIdx = MSG_LowPriority - 2;
		else 
			m_pComposeBar->m_iPriorityIdx = MSG_NormalPriority - 2;
	}

	switch (MSG_GetHTMLAction(GetMsgPane())) {
		default:
		case MSG_HTMLAskUser:
			m_pComposeBar->m_pszMessageFormat = strdup(szLoadString(IDS_FORMAT_ASKME));
			break;
		case MSG_HTMLUseMultipartAlternative:
			m_pComposeBar->m_pszMessageFormat = strdup(szLoadString(IDS_FORMAT_BOTH));
			break;
		case MSG_HTMLConvertToPlaintext:
			m_pComposeBar->m_pszMessageFormat = strdup(szLoadString(IDS_FORMAT_PLAIN));
			break;
		case MSG_HTMLSendAsHTML:
			m_pComposeBar->m_pszMessageFormat = strdup(szLoadString(IDS_FORMAT_HTML));
			break;
	}

    m_pComposeBar->SetAttachMyCard(MSG_GetCompBoolHeader(GetMsgPane(),MSG_ATTACH_VCARD_BOOL_HEADER_MASK));
    m_pComposeBar->SetReturnReceipt(MSG_GetCompBoolHeader(GetMsgPane(),MSG_RETURN_RECEIPT_BOOL_HEADER_MASK));
    m_pComposeBar->SetUseUUENCODE(MSG_GetCompBoolHeader(GetMsgPane(),MSG_UUENCODE_BINARY_BOOL_HEADER_MASK));
    UpdateSecurityOptions();

    m_bInitialized = TRUE;
}

void CComposeFrame::UpdateSecurityOptions(void)
{
    m_pComposeBar->SetSigned(MSG_GetCompBoolHeader(GetMsgPane(),MSG_SIGNED_BOOL_HEADER_MASK));
    m_pComposeBar->SetEncrypted(MSG_GetCompBoolHeader(GetMsgPane(),MSG_ENCRYPTED_BOOL_HEADER_MASK));
}

void CComposeFrame::GoldDoneLoading ()
{
   MSG_InitializeCompositionPane(GetMsgPane(),m_pOldContext,m_pFields);
   PostMessage(WM_COMP_SET_INITIAL_FOCUS);
   UpdateAttachmentInfo();
}

////////////////////////////////////////////////////////////////////////////////////
// Begin the pseudo-backend code here.

const char * MSG_HeaderMaskToString(MSG_HEADER_SET header)
{
    const char * retval = NULL;
    switch (header)
    {
	    case MSG_TO_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSTO);
	        break;
	    case MSG_CC_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSCC);
	        break;
	    case MSG_BCC_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSBCC);
	        break;
	    case MSG_REPLY_TO_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSREPLYTO);
	        break;
	    case MSG_NEWSGROUPS_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSNEWSGROUP);
	        break;
	    case MSG_FOLLOWUP_TO_HEADER_MASK:
	        retval = szLoadString(IDS_ADDRESSFOLLOWUPTO);
	        break;
	    default:
	        XP_ASSERT(0);
    }
    return retval;
}

MSG_HEADER_SET MSG_StringToHeaderMask(char * string)
{
    MSG_HEADER_SET retval = 0;
    if (!stricmp(szLoadString(IDS_ADDRESSTO),string))
        retval = MSG_TO_HEADER_MASK;
    else if (!stricmp(szLoadString(IDS_ADDRESSCC),string))
        retval = MSG_CC_HEADER_MASK;
    else if (!stricmp(szLoadString(IDS_ADDRESSBCC),string))
        retval = MSG_BCC_HEADER_MASK;
    else if (!stricmp(szLoadString(IDS_ADDRESSREPLYTO),string))
        retval = MSG_REPLY_TO_HEADER_MASK;
    else if (!stricmp(szLoadString(IDS_ADDRESSNEWSGROUP),string))
        retval = MSG_NEWSGROUPS_HEADER_MASK;
    else if (!stricmp(szLoadString(IDS_ADDRESSFOLLOWUPTO),string))
        retval = MSG_FOLLOWUP_TO_HEADER_MASK;
    else
        XP_ASSERT(0);
    return retval;
}

BOOL CComposeFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
   BOOL bRetVal = CGenericFrame::OnCmdMsg(nID,nCode,pExtra,pHandlerInfo);
   if (nCode == CN_UPDATE_COMMAND_UI)
      if (!bRetVal && !m_bUseHtml)
         bRetVal = m_Editor.OnCmdMsg(nID,nCode,pExtra,pHandlerInfo);
   return bRetVal;
}

void CComposeFrame::OnToggleMessageToolbar()
{
	GetChrome()->ShowToolbar(ID_COMPOSE_MESSAGE_TOOLBAR, !GetChrome()->GetToolbarVisible(ID_COMPOSE_MESSAGE_TOOLBAR));
}

void CComposeFrame::OnUpdateToggleMessageToolbar(CCmdUI* pCmdUI)
{
   if( pCmdUI->m_pMenu ){
      pCmdUI->m_pMenu->ModifyMenu(IDM_OPT_MESSAGEBAR_TOGGLE, MF_BYCOMMAND | MF_STRING, CASTUINT(IDM_OPT_MESSAGEBAR_TOGGLE),
                                  szLoadString(GetChrome()->GetToolbarVisible(ID_COMPOSE_MESSAGE_TOOLBAR) ?
                                               IDS_HIDE_MESSAGE_TOOLBAR : IDS_SHOW_MESSAGE_TOOLBAR ) );
   }   
   pCmdUI->Enable(TRUE);
}

void CComposeFrame::OnToggleAddressArea()
{
    m_pComposeBar->OnToggleShow();
}

void CComposeFrame::OnUpdateToggleAddressArea(CCmdUI * pCmdUI)
{
   //CLM: Change to a Show|Hide pair of strings
   if( pCmdUI->m_pMenu ){
      pCmdUI->m_pMenu->ModifyMenu(IDM_MESSAGEBODYONLY, MF_BYCOMMAND | MF_STRING, CASTUINT(IDM_MESSAGEBODYONLY),
                                  szLoadString(CASTUINT(m_pComposeBar->IsVisible() ?
                                               IDS_HIDE_ADDRESS_AREA : IDS_SHOW_ADDRESS_AREA)) );
   }   
   pCmdUI->Enable(TRUE);
}

#define UNSECURE_INDEX		9
#define SECURE_INDEX		11
#define UNSEC_SIGNED_INDEX	8
#define SEC_SIGNED_INDEX	10

void CComposeFrame::OnUpdateSecurity(CCmdUI * pCmdUI)
{
	XP_Bool bSigned = m_pComposeBar->GetSigned();
	XP_Bool bEncrypted = m_pComposeBar->GetEncrypted();
	LPNSTOOLBAR pIToolBar;
	m_pChrome->QueryInterface( IID_INSToolBar, (LPVOID *) &pIToolBar );
	if ( pIToolBar ) {
		CCommandToolbar *pToolBar = (CCommandToolbar *) CWnd::FromHandlePermanent( pIToolBar->GetHWnd() );
		
		int index = bEncrypted ? 
					(bSigned ? SEC_SIGNED_INDEX : SECURE_INDEX) :
				    (bSigned ? UNSEC_SIGNED_INDEX : UNSECURE_INDEX);

		pToolBar->ReplaceButtonBitmapIndex(ID_SECURITY, index);
		pIToolBar->Release();
	}
   OnUpdateButtonGeneral(pCmdUI);
}

void CComposeFrame::OnUpdateSecureStatus(CCmdUI *pCmdUI)
{
	XP_Bool bSigned = m_pComposeBar->GetSigned();
	XP_Bool bEncrypted = m_pComposeBar->GetEncrypted();

	if (pCmdUI->m_nID == IDS_SECURITY_STATUS)
		pCmdUI->Enable(bEncrypted);
	else
		pCmdUI->Enable(bSigned);
}

char * CComposeFrame::PromptMessageSubject()
{
    CWnd * pWnd = GetFocus();
    CString csText;
    CString csDefault;
    csText.LoadString(IDS_COMPOSE_NOSUBJECT);
    csDefault.LoadString(IDS_COMPOSE_DEFAULTNOSUBJECT);
    MWContext * pContext = GetMainContext()->GetContext();
    char * ptr = ABSTRACTCX(pContext)->Prompt(pContext, csText, csDefault);
    pWnd->SetFocus();
    return ptr;
}

void CComposeFrame::OnSecurity()
{
    m_pComposeBar->UpdateHeaderInfo ( );
    CGenericFrame::OnSecurity();
}

void CComposeFrame::RefreshNewEncoding(int16 doc_csid, BOOL bSave )
{
	int16 win_csid = INTL_DocToWinCharSetID(doc_csid);
	INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo( GetMainContext()->GetContext() );

	INTL_SetCSIDocCSID( c, doc_csid );	
	INTL_SetCSIWinCSID( c, win_csid );
	
	SetCSID( doc_csid );
}

LRESULT CComposeFrame::OnFindReplace(WPARAM wParam, LPARAM lParam) 
{
   if (!m_bUseHtml)
   {
      return m_Editor.OnFindReplace(wParam,lParam);
   }
   return CGenericFrame::OnFindReplace(wParam,lParam);
}

void CComposeFrame::GetMessageString(UINT MenuID, CString& Message) const
{
#ifdef EDITOR
    // static function for both CEditFrame and CComposeFrame
    //  to get runtime-determined statusline strings for menus
    if( edt_GetMessageString(GetActiveView(), MenuID, Message) )
        return;
#endif

    CGenericFrame::GetMessageString(MenuID, Message);
}

// OnSetMessageString - Override of CGenericFrame's WM_SETMESSAGESTRING message handler. We need this 
// because the base class is supressing the dynamically-created menu help strings for Composer
LRESULT CComposeFrame::OnSetMessageString(WPARAM wParam, LPARAM lParam)
{
	if( edt_IsEditorDynamicMenu(wParam) )
		return CFrameWnd::OnSetMessageString(wParam, lParam);
    else
		return CGenericFrame::OnSetMessageString(wParam, lParam);
}

// rhp - Added for sending the message when the window is not shown
// This is to wait until attachments are loaded for MAPI Operations
void CComposeFrame::UpdateComposeWindowForMAPI(void) 
{ 
    if (GetMAPISendMode() == MAPI_SEND)
    {
      PostMessage(WM_COMMAND, IDM_SEND); 
      SetMAPISendMode(MAPI_IGNORE);
    }
    else if (GetMAPISendMode() == MAPI_SAVE)
    {
      PostMessage(WM_COMMAND, IDM_SAVEASDRAFT); 
      SetMAPISendMode(MAPI_IGNORE);
    }
} 
