You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7280 lines
208 KiB
7280 lines
208 KiB
|
|
/*
|
|
* b o d y . c p p
|
|
*
|
|
* Purpose:
|
|
* base class implementation of Body object. Derrives from CDocHost to host the trident
|
|
* control.
|
|
*
|
|
* History
|
|
* August '96: brettm - created
|
|
*
|
|
* Copyright (C) Microsoft Corp. 1995, 1996.
|
|
*/
|
|
#include <pch.hxx>
|
|
#include "dllmain.h"
|
|
#include <shfusion.h>
|
|
#include "resource.h"
|
|
#include "strconst.h"
|
|
#include "htmlstr.h"
|
|
#include "mimeolep.h"
|
|
#include "mimeutil.h"
|
|
#include "htiframe.h" // ITargetFrame2
|
|
#include "htiface.h" // ITargetFramePriv
|
|
#include "vervec.h" // IVersion*
|
|
#include "triutil.h"
|
|
#include "util.h"
|
|
// #include "dochost.h"
|
|
#ifdef PLUSPACK
|
|
#include "htmlsp.h"
|
|
#endif //PLUSPACK
|
|
#include "body.h"
|
|
#include "bodyutil.h"
|
|
#include "oleutil.h"
|
|
#include "secmgr.h"
|
|
#include "mhtml.h"
|
|
#include "fmtbar.h"
|
|
#include "fontcash.h"
|
|
#include "attmenu.h"
|
|
#include "saveatt.h"
|
|
#include "frames.h"
|
|
#include "richedit.h"
|
|
#include "viewsrc.h"
|
|
#include "spell.h"
|
|
#include "tags.h"
|
|
#include "optary.h"
|
|
#include "shlwapip.h"
|
|
#include <icutil.h>
|
|
#include <demand.h>
|
|
|
|
ASSERTDATA
|
|
|
|
/*
|
|
* m a c r o s
|
|
*/
|
|
#define SetMenuItem(hmenu, id, fOn) EnableMenuItem(hmenu, id, (fOn)?MF_ENABLED:MF_DISABLED|MF_GRAYED);
|
|
|
|
/*
|
|
* c o n s t a n t s
|
|
*/
|
|
#define BKGRNDSPELL_TICKTIME 100
|
|
#define AUTODETECT_CHUNK 16384
|
|
#define AUTODETECT_TICKTIME 200
|
|
#define AUTODETECT_TIMEOUT 10
|
|
//#define USE_ABORT_TIMER
|
|
|
|
#define idTimerAutoDetect 110
|
|
#define idTimerBkgrndSpell 111
|
|
|
|
static WCHAR c_szMailToW[] =L"mailto:",
|
|
c_szOECmdW[] =L"oecmd:",
|
|
c_szHttpW[] =L"http://",
|
|
c_szFileW[] =L"file://";
|
|
|
|
#define CX_LABEL_PADDING 4
|
|
#define CY_LINE_PADDING 4
|
|
|
|
#define COLOR_HEADER COLOR_3DFACE
|
|
#define COLOR_HEADERTXT COLOR_BTNTEXT
|
|
#define COLOR_HEADERFOCUS COLOR_HIGHLIGHT
|
|
#define COLOR_HEADERTXTFOCUS COLOR_HIGHLIGHTTEXT
|
|
|
|
#define SMALLHEADERHEIGHT 3
|
|
#define CX_PANEICON 30
|
|
#define CY_PANEICON 30
|
|
#define CY_PANEPADDING (2*GetSystemMetrics(SM_CYBORDER))
|
|
#define CX_PANEPADDING (2*GetSystemMetrics(SM_CXBORDER))
|
|
|
|
enum
|
|
{
|
|
IBTF_INSERTATEND = 0x0001,
|
|
IBTF_URLHIGHLIGHT = 0x0002
|
|
};
|
|
|
|
#define idcTabs 999
|
|
#define idcSrcEdit 998
|
|
|
|
#define HDRTXT_BOLD 0x01
|
|
#define HDRTXT_SYSTEMFONT 0x02
|
|
|
|
/*
|
|
* t y p e d e f s
|
|
*/
|
|
class CVerHost :
|
|
public IVersionHost
|
|
{
|
|
public:
|
|
CVerHost();
|
|
virtual ~CVerHost();
|
|
|
|
|
|
// IUnknown
|
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID FAR *);
|
|
virtual ULONG STDMETHODCALLTYPE AddRef();
|
|
virtual ULONG STDMETHODCALLTYPE Release();
|
|
|
|
// IVersionHost
|
|
virtual HRESULT STDMETHODCALLTYPE QueryUseLocalVersionVector(BOOL *fUseLocal);
|
|
virtual HRESULT STDMETHODCALLTYPE QueryVersionVector(IVersionVector *pVersion);
|
|
|
|
|
|
private:
|
|
ULONG m_cRef;
|
|
};
|
|
|
|
|
|
/*
|
|
* g l o b a l s
|
|
*/
|
|
|
|
static const TCHAR c_szCaretSpanTag[] = "<SPAN id=\"__#Ath#CaretPos__\"> </SPAN>",
|
|
c_szCaretSpan[] = "__#Ath#CaretPos__",
|
|
c_szSignatureSpanTag[] = " <SPAN id=\"__#Ath#SignaturePos__\"></SPAN> ",
|
|
c_szSignatureSpan[] = "__#Ath#SignaturePos__",
|
|
c_szSigPrefix[] = "\r\n-- \r\n";
|
|
|
|
/*
|
|
* f u n c t i o n p r o t y p e s
|
|
*/
|
|
HRESULT CALLBACK FreeDataObj(PDATAOBJINFO pDataObjInfo, DWORD celt);
|
|
HRESULT HrSniffUrlForRfc822(LPWSTR pszUrlW);
|
|
|
|
/*
|
|
* f u n c t i o n s
|
|
*/
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
CBody::CBody()
|
|
{
|
|
m_dwReadyState = READYSTATE_UNINITIALIZED;
|
|
m_pszUrlW = NULL;
|
|
m_pMsg = NULL;
|
|
m_pMsgW = NULL;
|
|
m_pDoc = NULL;
|
|
m_hCharset=NULL;
|
|
m_dwNotify=0;
|
|
m_pTempFileUrl=NULL;
|
|
m_pParentDocHostUI=NULL;
|
|
m_pParentCmdTarget=NULL;
|
|
m_pParentInPlaceSite=0;
|
|
m_pParentInPlaceFrame=0;
|
|
m_fPlainMode=FALSE;
|
|
m_uHdrStyle = MESTYLE_NOHEADER ;
|
|
m_pszLayout = NULL;
|
|
m_pszFrom = 0;
|
|
m_pszTo = 0;
|
|
m_pszCc = 0;
|
|
m_pszSubject = 0;
|
|
m_fEmpty = 1;
|
|
m_fMessageParsed=0;
|
|
m_fDirty=0;
|
|
m_fDesignMode=0;
|
|
m_fAutoDetect=0;
|
|
m_fOnImage=0;
|
|
m_fTabLinks=0;
|
|
m_pFmtBar=NULL;
|
|
m_fLoading=1;
|
|
m_fForceCharsetLoad=FALSE;
|
|
m_pRangeIgnoreSpell=0;
|
|
m_pFontCache=0;
|
|
m_pDocActiveObj = 0;
|
|
m_pAttMenu=NULL;
|
|
m_hwndBtnBar=NULL;
|
|
m_hIml=0;
|
|
m_hImlHot=0;
|
|
m_cVisibleBtns=0;
|
|
m_pAttMenu=NULL;
|
|
m_pSecMgr = NULL;
|
|
m_hwndTab=NULL;
|
|
m_hwndSrc=NULL;
|
|
m_uSrcView = 0;
|
|
m_pSrcView=NULL;
|
|
m_fSrcTabs = 0;
|
|
m_fReloadingSrc = FALSE;
|
|
m_pSpell = 0;
|
|
m_fBkgrndSpelling = FALSE;
|
|
m_cchTotal = 0;
|
|
m_dwFontCacheNotify = 0;
|
|
m_fWasDirty = 0;
|
|
m_pDispContext=0;
|
|
m_dwContextItem=0;
|
|
m_pstmHtmlSrc=NULL;
|
|
m_pHashExternal = NULL;
|
|
m_dwAutoTicks = 0;
|
|
m_pAutoStartPtr = 0;
|
|
m_fIgnoreAccel = 0;
|
|
#ifdef PLUSPACK
|
|
m_pBkgSpeller = NULL;
|
|
#endif //PLUSPACK
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
CBody::~CBody()
|
|
{
|
|
Assert (m_pDispContext==NULL);
|
|
SafeRelease(m_pAttMenu);
|
|
SafeRelease(m_pParentInPlaceSite);
|
|
SafeRelease(m_pParentInPlaceFrame);
|
|
SafeRelease(m_pFmtBar);
|
|
SafeRelease(m_pRangeIgnoreSpell);
|
|
SafeRelease(m_pFontCache);
|
|
SafeRelease(m_pSecMgr);
|
|
SafeRelease(m_pSrcView);
|
|
SafeRelease(m_pstmHtmlSrc);
|
|
SafeRelease(m_pHashExternal);
|
|
SafeRelease(m_pAutoStartPtr);
|
|
|
|
Assert(m_pMsg==NULL);
|
|
Assert(m_pMsgW==NULL);
|
|
Assert(m_pTempFileUrl==NULL);
|
|
Assert(m_pszFrom == 0);
|
|
Assert(m_pszTo == 0);
|
|
Assert(m_pszCc == 0);
|
|
Assert(m_pszSubject == 0);
|
|
SafeMemFree(m_pszLayout);
|
|
if (m_hIml)
|
|
ImageList_Destroy(m_hIml);
|
|
if (m_hImlHot)
|
|
ImageList_Destroy(m_hImlHot);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Init
|
|
//
|
|
// Synopsis: pHostInfo is used to set parent's sites and frames.
|
|
// the parent inplace site is sent notifications on activation etc
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Init(HWND hwndParent, DWORD dwFlags, LPRECT prc, PBODYHOSTINFO pHostInfo)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::Init");
|
|
|
|
if (pHostInfo)
|
|
{
|
|
ReplaceInterface(m_pParentInPlaceSite, pHostInfo->pInPlaceSite);
|
|
ReplaceInterface(m_pParentInPlaceFrame, pHostInfo->pInPlaceFrame);
|
|
ReplaceInterface(m_pDocActiveObj, pHostInfo->pDoc);
|
|
if (m_pParentInPlaceSite)
|
|
{
|
|
// get the dochostUIhandler to delegate to when setparent sites is called
|
|
Assert(m_pParentDocHostUI==NULL);
|
|
Assert(m_pParentCmdTarget==NULL);
|
|
m_pParentInPlaceSite->QueryInterface(IID_IDocHostUIHandler, (LPVOID *)&m_pParentDocHostUI);
|
|
m_pParentInPlaceSite->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&m_pParentCmdTarget);
|
|
}
|
|
}
|
|
|
|
m_dwStyle=dwFlags;
|
|
|
|
hr = CDocHost::Init(hwndParent, dwFlags&MEBF_OUTERCLIENTEDGE, prc);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = HrCreateFormatBar(m_hwnd, idcFmtBar, dwFlags&MEBF_FORMATBARSEP, &m_pFmtBar);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// fire-up trident at init time
|
|
hr = EnsureLoaded();
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Close
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Close()
|
|
{
|
|
IConnectionPoint *pCP;
|
|
|
|
TraceCall("CBody::Close");
|
|
|
|
UnloadAll();
|
|
RegisterLoadNotify(FALSE);
|
|
|
|
#ifdef PLUSPACK
|
|
SafeRelease(m_pBkgSpeller);
|
|
#endif //PLUSPACK
|
|
|
|
#ifdef BACKGROUNDSPELL
|
|
if (m_pSpell && m_fBkgrndSpelling)
|
|
{
|
|
m_pSpell->HrRegisterKeyPressNotify(FALSE);
|
|
KillTimer(m_hwnd, idTimerBkgrndSpell); // done. Stop the timer
|
|
}
|
|
#endif // BACKGROUNDSPELL
|
|
|
|
// [email protected] - moved this from destructor - 31463 & 36253
|
|
if(m_pSpell)
|
|
m_pSpell->CloseSpeller();
|
|
SafeRelease(m_pSpell);
|
|
|
|
SafeRelease(m_pFmtBar);
|
|
SafeRelease(m_pDoc);
|
|
SafeRelease(m_pParentCmdTarget);
|
|
SafeRelease(m_pParentDocHostUI);
|
|
SafeRelease(m_pParentInPlaceSite);
|
|
SafeRelease(m_pParentInPlaceFrame);
|
|
SafeRelease(m_pDocActiveObj);
|
|
|
|
if (m_pFontCache &&
|
|
m_pFontCache->QueryInterface(IID_IConnectionPoint, (LPVOID *)&pCP)==S_OK)
|
|
{
|
|
pCP->Unadvise(m_dwFontCacheNotify);
|
|
pCP->Release();
|
|
}
|
|
SafeRelease(m_pFontCache);
|
|
|
|
CloseDocObj();
|
|
if (m_hwnd)
|
|
{
|
|
DestroyWindow(m_hwnd);
|
|
m_hwnd=NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: WndProc
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
LRESULT CBody::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HWND hwndT;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_WININICHANGE:
|
|
case WM_DISPLAYCHANGE:
|
|
case WM_SYSCOLORCHANGE:
|
|
case WM_QUERYNEWPALETTE:
|
|
case WM_PALETTECHANGED:
|
|
if (m_pFmtBar &&
|
|
m_pFmtBar->GetWindow(&hwndT)==S_OK)
|
|
SendMessage(hwndT, msg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if (!m_fFocus)
|
|
SetFocus(m_hwnd);
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
case WM_SETFOCUS:
|
|
OnFocus(msg==WM_SETFOCUS);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
// since we do blit stuff we invalidate here
|
|
InvalidateRect(m_hwnd, NULL, FALSE);
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
OnEraseBkgnd((HDC)wParam);
|
|
return TRUE;
|
|
|
|
case WM_CREATE:
|
|
if (FAILED(OnWMCreate()))
|
|
return -1;
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if (wParam == idTimerAutoDetect)
|
|
{
|
|
AutoDetectTimer();
|
|
return 0;
|
|
}
|
|
|
|
#ifdef BACKGROUNDSPELL
|
|
if (wParam == idTimerBkgrndSpell)
|
|
{
|
|
if (m_pSpell)
|
|
m_pSpell->HrBkgrndSpellTimer();
|
|
return 0;
|
|
}
|
|
#endif // BACKGROUNDSPELL
|
|
|
|
if (m_pSrcView &&
|
|
m_pSrcView->OnTimer(wParam)==S_OK)
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
if(OnWMCommand(
|
|
GET_WM_COMMAND_HWND(wParam, lParam),
|
|
GET_WM_COMMAND_ID(wParam, lParam),
|
|
GET_WM_COMMAND_CMD(wParam, lParam))==S_OK)
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
return WMNotify(wParam, (NMHDR*)lParam);
|
|
|
|
case WM_PAINT:
|
|
if (OnPaint()==S_OK)
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
return CDocHost::WndProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: QueryInterface
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::QueryInterface(REFIID riid, LPVOID FAR *lplpObj)
|
|
{
|
|
TraceCall("CBody::QueryInterface");
|
|
|
|
if(!lplpObj)
|
|
return E_INVALIDARG;
|
|
|
|
*lplpObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
*lplpObj = (IUnknown *)(IPropertyNotifySink *)this;
|
|
else if (IsEqualIID(riid, IID_IPropertyNotifySink))
|
|
*lplpObj = (IPropertyNotifySink *)this;
|
|
else if (IsEqualIID(riid, IID_IDocHostUIHandler))
|
|
*lplpObj = (IDocHostUIHandler*) this;
|
|
else if (IsEqualIID(riid, IID_IPersistMime))
|
|
*lplpObj = (IPersistMime*) this;
|
|
else if (IsEqualIID(riid, IID_ITargetFramePriv))
|
|
*lplpObj = (ITargetFramePriv*) this;
|
|
else if (IsEqualIID(riid, IID_IPersistMoniker))
|
|
*lplpObj = (LPVOID)(IPersistMoniker *)this;
|
|
else if (IsEqualIID(riid, IID_IFontCacheNotify))
|
|
*lplpObj = (LPVOID)(IFontCacheNotify *)this;
|
|
#if 0
|
|
else if (IsEqualIID(riid, IID_IDispatch))
|
|
*lplpObj = (LPVOID)(IDispatch *)this;
|
|
#endif
|
|
else
|
|
return CDocHost::QueryInterface(riid, lplpObj);
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: AddRef
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
ULONG CBody::AddRef()
|
|
{
|
|
TraceCall("CBody::AddRef");
|
|
return CDocHost::AddRef();
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Release
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
ULONG CBody::Release()
|
|
{
|
|
TraceCall("CBody::Release");
|
|
return CDocHost::Release();
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: QueryService
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
IServiceProvider *pSP;
|
|
IVersionHost *pVersion;
|
|
|
|
//DebugPrintInterface(riid, "CBody::QueryService");
|
|
|
|
// delegate to the mimeedit host first
|
|
if (m_pParentInPlaceSite)
|
|
{
|
|
if (m_pParentInPlaceSite->QueryInterface(IID_IServiceProvider, (LPVOID *)&pSP)==S_OK)
|
|
{
|
|
hr = pSP->QueryService(guidService, riid, ppvObject);
|
|
pSP->Release();
|
|
if (hr==S_OK)
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID(guidService, SID_SInternetSecurityManager))
|
|
{
|
|
if (!m_pSecMgr)
|
|
CreateSecurityManger(m_pParentCmdTarget, &m_pSecMgr);
|
|
|
|
if (m_pSecMgr)
|
|
return m_pSecMgr->QueryInterface(riid, ppvObject);
|
|
}
|
|
else if (IsEqualGUID(guidService, IID_ITargetFrame2))
|
|
return QueryInterface(riid, ppvObject);
|
|
else if (IsEqualGUID(guidService, SID_SVersionHost) && IsEqualIID(riid, IID_IVersionHost))
|
|
{
|
|
pVersion = new CVerHost();
|
|
if (!pVersion)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*ppvObject = (LPVOID)(IVersionHost *)pVersion;
|
|
return S_OK;
|
|
}
|
|
|
|
return CDocHost::QueryService(guidService, riid, ppvObject);
|
|
}
|
|
|
|
// *** IDocHostUIHandler ***
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetHostInfo
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetHostInfo( DOCHOSTUIINFO* pInfo )
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::GetHostInfo");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->GetHostInfo(pInfo);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
|
|
pInfo->dwFlags = DOCHOSTUIFLAG_DIV_BLOCKDEFAULT|DOCHOSTUIFLAG_OPENNEWWIN|
|
|
DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY|
|
|
DOCHOSTUIFLAG_IME_ENABLE_RECONVERSION |
|
|
DOCHOSTUIFLAG_CODEPAGELINKEDFONTS;
|
|
|
|
//This sets the flags that match the browser's encoding
|
|
fGetBrowserUrlEncoding(&pInfo->dwFlags);
|
|
|
|
if (!(m_dwStyle & MEBF_INNERCLIENTEDGE))
|
|
pInfo->dwFlags |= DOCHOSTUIFLAG_NO3DBORDER;
|
|
|
|
if (m_dwStyle & MEBF_NOSCROLL)
|
|
pInfo->dwFlags |= DOCHOSTUIFLAG_SCROLL_NO;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: ShowUI
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject,
|
|
IOleCommandTarget *pCommandTarget,
|
|
IOleInPlaceFrame *pFrame,
|
|
IOleInPlaceUIWindow *pDoc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::ShowUI");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: HideUI
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::HideUI(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::HideUI");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->HideUI();
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: UpdateUI
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::UpdateUI(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::UpdateUI");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->UpdateUI();
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: EnableModeless
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::EnableModeless(BOOL fEnable)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::EnableModeless");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->EnableModeless(fEnable);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnDocWindowActivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnDocWindowActivate(BOOL fActivate)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::OnDocWindowActivate");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->OnDocWindowActivate(fActivate);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnFrameWindowActivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnFrameWindowActivate(BOOL fActivate)
|
|
{
|
|
HRESULT hr;
|
|
TraceCall("CBody::OnFrameWindowActivate");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->OnFrameWindowActivate(fActivate);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: ResizeBorder
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::ResizeBorder(LPCRECT prcBorder,
|
|
IOleInPlaceUIWindow* pUIWindow,
|
|
BOOL fRameWindow)
|
|
{
|
|
HRESULT hr;
|
|
TraceCall("CBody::ResizeBorder");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->ResizeBorder(prcBorder, pUIWindow, fRameWindow);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: ShowContextMenu
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::ShowContextMenu( DWORD dwID,
|
|
POINT *pptPosition,
|
|
IUnknown *pcmdtReserved,
|
|
IDispatch *pDispatchObjectHit)
|
|
{
|
|
HRESULT hr;
|
|
HMENU hMenu=0;
|
|
INT id;
|
|
IHTMLTxtRange *pTxtRange=0;
|
|
#ifdef PLUSPACK
|
|
ISpellingSuggestions * pSuggestions = NULL;
|
|
IMarkupPointer * pPointerLeft = NULL;
|
|
IMarkupPointer * pPointerRight = NULL;
|
|
IMarkupServices * pMarkupServices = NULL;
|
|
IDisplayPointer * pDispPointer = NULL;
|
|
IDisplayServices * pDisplayServices = NULL;
|
|
IHTMLElement * pElement = NULL;
|
|
IHTMLBodyElement * pBody = NULL;
|
|
IHTMLTxtRange *pRange = NULL;
|
|
IHTMLWindow2 *pWindow = NULL;
|
|
IHTMLEventObj *pEvent = NULL;
|
|
IHTMLSelectionObject * pSelection = NULL;
|
|
VARIANT_BOOL fInSquiggle = VARIANT_FALSE;
|
|
VARIANT var;
|
|
BSTR bstrSuggestion = NULL;
|
|
BSTR bstrWord = NULL;
|
|
BSTR bstrSelectionType = NULL;
|
|
TCHAR szAnsiSuggestion[256];
|
|
INT cch;
|
|
BOOL fRepeatWord;
|
|
LONG lCount = 0;
|
|
MENUITEMINFO mii = {0};
|
|
|
|
LONG lButton;
|
|
int i;
|
|
#else
|
|
BOOL fSpellSuggest=FALSE;
|
|
#endif //PLUSPACK
|
|
|
|
TraceCall("CBody::ShowContextMenu");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->ShowContextMenu(dwID, pptPosition, pcmdtReserved, pDispatchObjectHit);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
Assert(m_lpOleObj && m_hwnd);
|
|
|
|
m_dwContextItem = dwID;
|
|
#ifdef PLUSPACK
|
|
hr = GetSelection(&pTxtRange);
|
|
|
|
// Background spell context menu
|
|
if(m_fDesignMode && m_pBkgSpeller && m_pDoc)
|
|
{
|
|
// Check for squiggle
|
|
//
|
|
CHECKHR(hr = m_pBkgSpeller->IsInSquiggle(pTxtRange, &fInSquiggle) );
|
|
|
|
if (fInSquiggle)
|
|
{ //only if we have a suggestion
|
|
// Get suggestions
|
|
//
|
|
CHECKHR(hr = m_pBkgSpeller->GetSpellingSuggestions(pTxtRange, FALSE, &pSuggestions) );
|
|
|
|
//
|
|
// Create the context menu
|
|
//
|
|
|
|
if (!(hMenu = LoadPopupMenu(idmrCtxtSpellSuggest)))
|
|
{
|
|
hr = TraceResult(E_FAIL);
|
|
goto exit;
|
|
}
|
|
/* CHECKHR(hr = pSuggestions->get_IsDoubleWord(&fRepeatWord) );
|
|
if (fRepeatWord)
|
|
{
|
|
// if (!AppendMenuA(hMenu, MF_STRING, IDM_DELETEWORD, "&Delete Repeated Word"))
|
|
// goto exit;
|
|
|
|
}
|
|
else
|
|
{ */
|
|
//
|
|
// Fill with suggestions
|
|
//
|
|
CHECKHR(hr = pSuggestions->get_Count(&lCount) );
|
|
|
|
if (lCount < 1)
|
|
{
|
|
if (!AppendMenuA(hMenu, MF_DISABLED | MF_GRAYED, 1, "(no suggestions)"))
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
V_VT(&var) = VT_I4;
|
|
for (i = 0; (i <= lCount) && ((i + idmSuggest0) <= idmSuggest4); ++i)
|
|
{
|
|
V_I4(&var) = i + 1; // get_Item starts from 1
|
|
|
|
SysFreeString(bstrSuggestion);
|
|
bstrSuggestion = NULL;
|
|
CHECKHR(hr = pSuggestions->get_Item(&var, &bstrSuggestion));
|
|
|
|
cch = WideCharToMultiByte(0, 0, bstrSuggestion, SysStringLen(bstrSuggestion), szAnsiSuggestion, ARRAYSIZE(szAnsiSuggestion), NULL, NULL);
|
|
if (!cch)
|
|
goto exit;
|
|
|
|
szAnsiSuggestion[cch] = 0;
|
|
|
|
// Initialize the menu info
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_ID | MIIM_TYPE;
|
|
mii.fType = MFT_STRING;
|
|
mii.fState = MFS_ENABLED;
|
|
mii.wID = i + idmSuggest0;
|
|
mii.dwTypeData = szAnsiSuggestion;
|
|
mii.cch = lstrlen(szAnsiSuggestion);
|
|
|
|
if(!InsertMenuItem(hMenu, 0, TRUE, &mii))
|
|
// if (!AppendMenuA(hMenu, MF_STRING, i + idmSuggest0, szAnsiSuggestion))
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// if (!AppendMenuA(hMenu, MF_SEPARATOR, 0, NULL))
|
|
// goto exit;
|
|
|
|
// if (!AppendMenuA(hMenu, MF_STRING, IDM_IGNOREWORD, "&Ignore All"))
|
|
// goto exit;
|
|
}
|
|
|
|
}
|
|
#endif //PLUSPACK
|
|
|
|
#ifdef BACKGROUNDSPELL
|
|
if (m_pSpell && m_fBkgrndSpelling)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = GetSelection(&pTxtRange);
|
|
if (pTxtRange)
|
|
{
|
|
if (m_pSpell->HrHasSquiggle(pTxtRange)==S_OK)
|
|
{
|
|
if (!(hMenu = LoadPopupMenu(idmrCtxtSpellSuggest)))
|
|
{
|
|
hr = TraceResult(E_FAIL);
|
|
goto exit;
|
|
}
|
|
|
|
hr = m_pSpell->HrInsertMenu(hMenu, pTxtRange);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
fSpellSuggest = TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif // BACKGROUNDSPELL
|
|
|
|
#ifdef PLUSPACK
|
|
if (!fInSquiggle)
|
|
#else
|
|
if (!fSpellSuggest)
|
|
#endif //PLUSPACK
|
|
{
|
|
if (!(hMenu = LoadPopupMenu(m_fDesignMode?idmrCtxtEditMode:idmrCtxtBrowseMode)))
|
|
{
|
|
hr = TraceResult(E_FAIL);
|
|
goto exit;
|
|
}
|
|
|
|
if (!m_fDesignMode)
|
|
{
|
|
// if in browse mode, query the host to see if we can provider add to WAB and
|
|
// add to fave menu items
|
|
if (dwID == CONTEXT_MENU_ANCHOR)
|
|
{
|
|
AppendAnchorItems(hMenu, pDispatchObjectHit);
|
|
}
|
|
else
|
|
{
|
|
// remove the CopyShortCut command if it's not an anchor
|
|
RemoveMenu(hMenu, idmCopyShortcut, MF_BYCOMMAND);
|
|
// remove the SaveTargetAs command if it's not an anchor
|
|
RemoveMenu(hMenu, idmSaveTargetAs, MF_BYCOMMAND);
|
|
}
|
|
|
|
if (dwID != CONTEXT_MENU_IMAGE)
|
|
EnableMenuItem(hMenu, idmSavePicture, MF_BYCOMMAND|MF_GRAYED);
|
|
}
|
|
else
|
|
{
|
|
// if in editmode, trident does not pass dwID==CONTEXT_MENU_ANCHOR so we have to
|
|
// test to see if the selection in an anchor to set this. It may fix this in the future
|
|
// so code for both cases
|
|
|
|
if (dwID==0 && GetSelectedAnchor(NULL)==S_OK)
|
|
dwID = CONTEXT_MENU_ANCHOR;
|
|
|
|
#ifdef FOLLOW_LINK
|
|
// if edit-mode, and not on an anchor, hide the openlink menu command
|
|
if (dwID != CONTEXT_MENU_ANCHOR)
|
|
RemoveMenu(hMenu, idmOpenLink, MF_BYCOMMAND);
|
|
#endif
|
|
}
|
|
|
|
m_fOnImage = !!(dwID == CONTEXT_MENU_IMAGE);
|
|
UpdateContextMenu(hMenu, (dwID == CONTEXT_MENU_IMAGE || dwID == CONTEXT_MENU_ANCHOR), pDispatchObjectHit);
|
|
}
|
|
|
|
id = (INT)TrackPopupMenu(
|
|
hMenu,
|
|
TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
|
|
pptPosition->x,
|
|
pptPosition->y,
|
|
0,
|
|
GetParent(m_hwnd),
|
|
NULL);
|
|
|
|
// we have to use TPM_RETURNCMD here as we need to process the command-id before returning from this
|
|
// function, other wise trident will be confused about the object being clicked on.
|
|
#ifdef PLUSPACK
|
|
if (pTxtRange && id!=0 && m_pBkgSpeller && fInSquiggle)
|
|
{
|
|
//
|
|
// Handle command
|
|
//
|
|
|
|
switch (id)
|
|
{
|
|
case idmIgnoreAll:
|
|
CHECKHR(hr = m_pBkgSpeller->IgnoreWord(pTxtRange) );
|
|
break;
|
|
|
|
/*case IDM_DELETEWORD:
|
|
{
|
|
SysFreeString(bstrSuggestion);
|
|
bstrSuggestion = SysAllocString(L"");
|
|
CHECKHR(hr = pTxtRange->put_text(bstrSuggestion) );
|
|
break;
|
|
} */
|
|
|
|
case idmIgnore:
|
|
{
|
|
//
|
|
// mark this as clean
|
|
//
|
|
CHECKHR(hr = m_pBkgSpeller->MarkRegion(pTxtRange, VARIANT_FALSE) );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if ((id - idmSuggest0) <= lCount)
|
|
{
|
|
V_I4(&var) = id - idmSuggest0 + 1; // get_Item starts from 1
|
|
|
|
SysFreeString(bstrSuggestion);
|
|
bstrSuggestion = NULL;
|
|
CHECKHR(hr = pSuggestions->get_Item(&var, &bstrSuggestion) );
|
|
|
|
CHECKHR(hr = pTxtRange->put_text(bstrSuggestion) );
|
|
/* {
|
|
CHAR szBuf[MAX_PATH] = {0};
|
|
BSTR bstr=0;
|
|
BSTR bstrPut=0;
|
|
LPSTR pch=0;
|
|
INT i=0;
|
|
cch = WideCharToMultiByte(0, 0, bstrSuggestion, SysStringLen(bstrSuggestion), szAnsiSuggestion, 255, NULL, NULL);
|
|
szAnsiSuggestion[cch] = '\0';
|
|
pch = szAnsiSuggestion;
|
|
StrCpyN(szBuf, pch, ARRAYSIZE(szBuf));
|
|
|
|
if (SUCCEEDED(pTxtRange->get_text(&bstr)) && bstr)
|
|
{
|
|
LPSTR pszText = 0;
|
|
if (SUCCEEDED(HrBSTRToLPSZ(CP_ACP, bstr, &pszText)) && pszText)
|
|
{
|
|
LPSTR psz;
|
|
INT nSpaces=0;
|
|
psz = StrChrI(pszText, ' ');
|
|
if(psz)
|
|
{
|
|
nSpaces = (INT) (&pszText[lstrlen(pszText)] - psz);
|
|
Assert(nSpaces>=0);
|
|
for(int i=0; i<(nSpaces-1); i++)
|
|
StrCatBuff(szBuf, " ", ARRAYSIZE(szBuf));
|
|
if (nSpaces>0)
|
|
StrCatBuff(szBuf, " ", ARRAYSIZE(szBuf));
|
|
}
|
|
hr = HrLPSZToBSTR(szBuf, &bstrPut);
|
|
|
|
SafeMemFree(pszText);
|
|
}
|
|
SafeSysFreeString(bstr);
|
|
}
|
|
if (bstrPut)
|
|
{
|
|
pTxtRange->pasteHTML(bstrPut);
|
|
SafeSysFreeString(bstrPut);
|
|
}
|
|
|
|
}*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
goto exit;
|
|
}
|
|
#else
|
|
if (pTxtRange && id!=0 && m_pSpell && fSpellSuggest && m_pSpell->OnWMCommand(id, pTxtRange)==S_OK)
|
|
goto exit;
|
|
#endif //PLUSPACK
|
|
|
|
// stuff the IDispatch object, so our WMCommand handler can use it
|
|
if (m_pDispContext = pDispatchObjectHit)
|
|
pDispatchObjectHit->AddRef();
|
|
|
|
if(id != 0)
|
|
OnWMCommand(NULL, id, 0);
|
|
|
|
exit:
|
|
SafeRelease(m_pDispContext);
|
|
#ifdef PLUSPACK
|
|
SysFreeString(bstrSuggestion);
|
|
SysFreeString(bstrWord);
|
|
SysFreeString(bstrSelectionType);
|
|
#endif //PLUSPACK
|
|
if (hMenu)
|
|
DestroyMenu(hMenu);
|
|
#ifdef PLUSPACK
|
|
SafeRelease(pSuggestions);
|
|
SafeRelease(pPointerLeft);
|
|
SafeRelease(pPointerRight);
|
|
SafeRelease(pMarkupServices);
|
|
SafeRelease(pDispPointer);
|
|
SafeRelease(pDisplayServices);
|
|
SafeRelease(pElement);
|
|
SafeRelease(pBody);
|
|
SafeRelease(pSelection);
|
|
SafeRelease(pWindow);
|
|
SafeRelease(pRange);
|
|
SafeRelease(pEvent);
|
|
#endif //PLUSPACK
|
|
ReleaseObj(pTxtRange);
|
|
|
|
m_dwContextItem = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetDropTarget
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::GetDropTarget");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->GetDropTarget(pDropTarget, ppDropTarget);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetExternal
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetExternal(IDispatch **ppDispatch)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::GetExternal");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->GetExternal(ppDispatch);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: TranslateUrl
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::TranslateUrl(DWORD dwTranslate, OLECHAR *pwszUrlIn, OLECHAR **ppwszUrlOut)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
LPSTR pszUrlIn=NULL;
|
|
LPSTR pszBodyUrl=NULL;
|
|
LPSTR pszFilePath=NULL;
|
|
LPSTR pszUrlOut=NULL;
|
|
LPSTR pszFree=NULL;
|
|
LPSTR pszGenFName=NULL;
|
|
LPSTR pszParameters=NULL;
|
|
LPSTR pszCommandLine=NULL;
|
|
CHAR szBuffer[MAX_PATH];
|
|
HBODY hBody;
|
|
IMimeBody *pBody=NULL;
|
|
IStream *pStream=NULL;
|
|
PROPVARIANT rVariant;
|
|
HANDLE hFile=INVALID_HANDLE_VALUE;
|
|
ULONG cbTotal;
|
|
CHAR szFilePath[MAX_PATH + MAX_PATH];
|
|
ULONG cch;
|
|
BOOL fReturnAbort=FALSE;
|
|
LPTEMPFILEINFO pTempFile;
|
|
SHELLEXECUTEINFO rExecute;
|
|
DWORD cc = 0;
|
|
|
|
TraceCall("CBody::TranslateUrl");
|
|
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->TranslateUrl(dwTranslate, pwszUrlIn, ppwszUrlOut);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
// Init
|
|
*ppwszUrlOut = NULL;
|
|
|
|
if (NULL == pwszUrlIn || NULL == ppwszUrlOut)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// No Message Object
|
|
if ((NULL == m_pMsg) || (NULL == m_pMsgW))
|
|
return S_FALSE;
|
|
|
|
// If pwszUrlIn is not already an mhtml: url
|
|
if (StrCmpNIW(pwszUrlIn, L"mhtml:", 6) != 0)
|
|
return S_FALSE;
|
|
|
|
// Convert To ANSI
|
|
pszUrlIn = PszToANSI(CP_ACP, pwszUrlIn);
|
|
if (!pszUrlIn)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
// UnEscape the Url
|
|
hr = UrlUnescapeA(pszUrlIn, NULL, NULL, URL_UNESCAPE_INPLACE);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Split It
|
|
hr = MimeOleParseMhtmlUrl(pszUrlIn, NULL, &pszBodyUrl);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Resolve the body url
|
|
hr = m_pMsg->ResolveURL(NULL, NULL, pszBodyUrl, 0, &hBody);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Get an IMimeBody
|
|
hr = m_pMsg->BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Abort
|
|
fReturnAbort = TRUE;
|
|
|
|
// If HTML, then write the entire message source to a temp file and return an mhtml url
|
|
if (pBody->IsContentType(STR_CNT_TEXT, STR_SUB_HTML) == S_OK)
|
|
{
|
|
// Get ixplorer.exe path
|
|
GetExePath(c_szIexploreExe, szBuffer, ARRAYSIZE(szBuffer), FALSE);
|
|
|
|
// Set command line
|
|
pszCommandLine = szBuffer;
|
|
|
|
// Get a Stream
|
|
hr = m_pMsg->GetMessageSource(&pStream, 0);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Init Variant
|
|
rVariant.vt = VT_LPSTR;
|
|
|
|
// Get a filename from the message object
|
|
hr = m_pMsg->GetProp(PIDTOSTR(PID_ATT_GENFNAME), 0, &rVariant);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Save pszFilePath
|
|
pszGenFName = rVariant.pszVal;
|
|
|
|
// Create temp file
|
|
hr = CreateTempFile(pszGenFName, c_szMHTMLExt, &pszFilePath, &hFile);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Write the stream to a file
|
|
hr = WriteStreamToFileHandle(pStream, hFile, &cbTotal);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Build: mhtml:(pszFilePath)!pszBodyUrl
|
|
cc = lstrlen(c_szMHTMLColon) + lstrlen(c_szFileUrl) + lstrlen(pszFilePath) + 1 + lstrlen(pszBodyUrl) + 1;
|
|
pszParameters = PszAllocA(cc);
|
|
if (!pszParameters)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
// Build pszParameters
|
|
wnsprintf(pszParameters, cc, "%s%s%s!%s", c_szMHTMLColon, c_szFileUrl, pszFilePath, pszBodyUrl);
|
|
}
|
|
|
|
// Otherwise, dump the body data to a temp file and return a url to it
|
|
else
|
|
{
|
|
// Get a Stream
|
|
hr = pBody->GetData(IET_INETCSET, &pStream);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Set sizeof szFilePath
|
|
cch = ARRAYSIZE(szFilePath);
|
|
|
|
// If cid:
|
|
if (StrCmpNIA(pszBodyUrl, "cid:", 4) == 0 || FAILED(PathCreateFromUrlA(pszBodyUrl, szFilePath, &cch, 0)))
|
|
{
|
|
// Init Variant
|
|
rVariant.vt = VT_LPSTR;
|
|
|
|
// Get a filename from the message object
|
|
hr = pBody->GetProp(PIDTOSTR(PID_ATT_GENFNAME), 0, &rVariant);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Save pszFilePath
|
|
pszGenFName = rVariant.pszVal;
|
|
|
|
// Create temp file
|
|
hr = CreateTempFile(pszGenFName, NULL, &pszFilePath, &hFile);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
}
|
|
else
|
|
{
|
|
// Create temp file
|
|
hr = CreateTempFile(szFilePath, NULL, &pszFilePath, &hFile);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
}
|
|
|
|
// Write the stream to a file
|
|
hr = WriteStreamToFileHandle(pStream, hFile, &cbTotal);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Build: file://(pszFilePath)
|
|
cc = lstrlen(c_szFileUrl) + lstrlen(pszFilePath) + 1;
|
|
pszUrlOut = PszAllocA(cc);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Build pszUrlOut
|
|
wnsprintf(pszUrlOut, cc, "%s%s", c_szFileUrl, pszFilePath);
|
|
|
|
// Set the pszCommandLine
|
|
pszCommandLine = pszUrlOut;
|
|
}
|
|
|
|
// Close the file - the file must get closed here (i.e. after the call to MimeOleCleanupTempFiles)
|
|
FlushFileBuffers(hFile);
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Is this file safe to run ?
|
|
hr = MimeEditIsSafeToRun(m_hwnd, pszFilePath, FALSE);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// SaveAs
|
|
if (MIMEEDIT_S_OPENFILE == hr)
|
|
{
|
|
// Locals
|
|
OPENFILENAME ofn;
|
|
TCHAR szTitle[CCHMAX_STRINGRES];
|
|
TCHAR szFilter[CCHMAX_STRINGRES];
|
|
TCHAR szFile[MAX_PATH];
|
|
|
|
// Init
|
|
*szFile=0;
|
|
*szFilter=0;
|
|
*szTitle=0;
|
|
|
|
// Copy filename
|
|
StrCpyN(szFile, pszFilePath, ARRAYSIZE(szFile));
|
|
|
|
// Init Open file structure
|
|
ZeroMemory (&ofn, sizeof(OPENFILENAME));
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = m_hwnd;
|
|
LoadString(g_hLocRes, idsFilterAttSave, szFilter, sizeof(szFilter));
|
|
ReplaceChars(szFilter, '|', '\0');
|
|
ofn.lpstrFilter = szFilter;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = sizeof (szFile);
|
|
LoadString(g_hLocRes, idsSaveAttachmentAs, szTitle, sizeof(szTitle));
|
|
ofn.lpstrTitle = szTitle;
|
|
ofn.Flags = OFN_NOCHANGEDIR | OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
|
|
|
// Show SaveAs Dialog
|
|
if (HrAthGetFileName(&ofn, FALSE) == S_OK)
|
|
{
|
|
// If the same...
|
|
if (lstrcmpi(pszFilePath, szFile) == 0)
|
|
{
|
|
// Just free pszfilePath so that we don't delete it
|
|
SafeMemFree(pszFilePath);
|
|
}
|
|
|
|
// Copy the file - Overwrite
|
|
else
|
|
CopyFile(pszFilePath, szFile, FALSE);
|
|
}
|
|
|
|
// Done
|
|
goto error;
|
|
}
|
|
|
|
// Must be trying to execute the file
|
|
hr = MimeEditVerifyTrust(m_hwnd, PathFindFileName(pszFilePath), pszFilePath);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// Setup the Shell Execute Structure
|
|
ZeroMemory (&rExecute, sizeof(SHELLEXECUTEINFO));
|
|
rExecute.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
rExecute.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
rExecute.hwnd = m_hwnd;
|
|
rExecute.nShow = SW_SHOWNORMAL;
|
|
rExecute.lpFile = pszCommandLine;
|
|
rExecute.lpVerb = NULL;
|
|
rExecute.lpParameters = pszParameters;
|
|
|
|
// Execute the File
|
|
TraceInfoSideAssert((0 != ShellExecuteEx(&rExecute)), _MSG("ShellExecuteEx failed - GetLastError() = %d\n", GetLastError()));
|
|
|
|
// Add the temp file to the list
|
|
if (SUCCEEDED(AppendTempFileList(&m_pTempFileUrl, pszFilePath, rExecute.hProcess)))
|
|
pszFilePath = NULL;
|
|
|
|
error:
|
|
// Cleanup
|
|
SafeRelease(pBody);
|
|
SafeRelease(pStream);
|
|
SafeMemFree(pszUrlIn);
|
|
SafeMemFree(pszBodyUrl);
|
|
SafeMemFree(pszFree);
|
|
SafeMemFree(pszUrlOut);
|
|
SafeMemFree(pszGenFName);
|
|
SafeMemFree(pszParameters);
|
|
|
|
// Close the file - the file must get closed here (i.e. after the call to MimeOleCleanupTempFiles)
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
CloseHandle(hFile);
|
|
|
|
// If we still have pszFilePath, delete the file
|
|
if (pszFilePath)
|
|
{
|
|
DeleteFile(pszFilePath);
|
|
g_pMalloc->Free(pszFilePath);
|
|
}
|
|
|
|
// Done
|
|
return (TRUE == fReturnAbort) ? E_ABORT : S_FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: FilterDataObject
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::FilterDataObject(IDataObject *pDO, IDataObject **ppDORet)
|
|
{
|
|
IDataObject *pDataObjNew = NULL;
|
|
HRESULT hr = S_FALSE;
|
|
STGMEDIUM stgmed;
|
|
DATAOBJINFO* pInfo = 0;
|
|
DATAOBJINFO* pInfoCopy = 0;
|
|
FORMATETC fetc = {0};
|
|
LPBYTE pCopy=0;
|
|
INT i, j, cFormats=0;
|
|
USHORT cfFormat[2] =
|
|
{CF_TEXT, CF_UNICODETEXT};
|
|
LPBYTE lpsz[2]={0};
|
|
ULONG lStreamLength[2]={0};
|
|
|
|
TraceCall("CBody::FilterDataObject");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->FilterDataObject(pDO, ppDORet);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
if (!m_fPlainMode || !m_fDesignMode)
|
|
return S_FALSE;
|
|
|
|
if (pDO==NULL || ppDORet==NULL)
|
|
return S_FALSE;
|
|
|
|
*ppDORet = NULL;
|
|
|
|
for (i=0; i<ARRAYSIZE(cfFormat); i++)
|
|
{
|
|
// get the plain-text
|
|
fetc.cfFormat=cfFormat[i];
|
|
fetc.dwAspect=DVASPECT_CONTENT;
|
|
fetc.tymed=TYMED_HGLOBAL;
|
|
fetc.lindex=0;
|
|
|
|
hr=pDO->QueryGetData(&fetc);
|
|
if(FAILED(hr))
|
|
continue;
|
|
|
|
ZeroMemory(&stgmed, sizeof(stgmed));
|
|
hr=pDO->GetData(&fetc, &stgmed);
|
|
if(FAILED(hr))
|
|
goto cleanloop;
|
|
|
|
Assert(stgmed.hGlobal);
|
|
|
|
// make a copy of the plain text string.
|
|
pCopy = (LPBYTE)GlobalLock(stgmed.hGlobal);
|
|
|
|
if (!pCopy)
|
|
{
|
|
hr=E_FAIL;
|
|
goto cleanloop;
|
|
}
|
|
|
|
if(fetc.cfFormat == CF_TEXT)
|
|
lStreamLength[i] = lstrlen((LPSTR)pCopy) + 1;
|
|
else
|
|
lStreamLength[i] = sizeof(WCHAR) * (lstrlenW((LPWSTR)pCopy)+1);
|
|
|
|
if (!MemAlloc((LPVOID*)&lpsz[i], lStreamLength[i]))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto cleanloop;
|
|
}
|
|
|
|
CopyMemory(lpsz[i], pCopy, lStreamLength[i]);
|
|
GlobalUnlock(stgmed.hGlobal);
|
|
cFormats++;
|
|
|
|
cleanloop:
|
|
// addref the pUnk as it will be release in releasestgmed
|
|
if(stgmed.pUnkForRelease)
|
|
stgmed.pUnkForRelease->AddRef();
|
|
ReleaseStgMedium(&stgmed);
|
|
if(FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
if(cFormats == 0)
|
|
{
|
|
hr = E_FAIL;
|
|
goto error;
|
|
}
|
|
|
|
if (!MemAlloc((LPVOID*)&pInfo, sizeof(DATAOBJINFO)*cFormats))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
ZeroMemory(pInfo, sizeof(DATAOBJINFO)*cFormats);
|
|
|
|
j = 0;
|
|
for(i=0; i<ARRAYSIZE(cfFormat); i++)
|
|
{
|
|
if(lpsz[i] != 0)
|
|
{
|
|
SETDefFormatEtc(pInfo[j].fe, cfFormat[i], TYMED_HGLOBAL);
|
|
pInfo[j].pData = lpsz[i];
|
|
pInfo[j].cbData = lStreamLength[i];
|
|
j++;
|
|
}
|
|
}
|
|
Assert(j == cFormats);
|
|
|
|
hr = CreateDataObject(pInfo, cFormats, (PFNFREEDATAOBJ)FreeDataObj, &pDataObjNew);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
//CDataObject will free this now it accepted it
|
|
for(i=0; i<ARRAYSIZE(cfFormat); i++)
|
|
lpsz[i]=NULL;
|
|
pInfo=NULL;
|
|
*ppDORet = pDataObjNew;
|
|
pDataObjNew = NULL;
|
|
|
|
error:
|
|
for(i=0; i<ARRAYSIZE(cfFormat); i++)
|
|
SafeMemFree(lpsz[i]);
|
|
SafeMemFree(pInfo);
|
|
ReleaseObj(pDataObjNew);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: TranslateAccelerator
|
|
//
|
|
// Synopsis:
|
|
// Trident calls the host first before handling an
|
|
// accelerator. If we return S_OK, it assumes we handled
|
|
// it and carries on. If we return S_FALSE it assumes
|
|
// we don't care, and it will do it's own action
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::TranslateAccelerator");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
// commands which are always available
|
|
switch (nCmdID)
|
|
{
|
|
case 0:
|
|
case IDM_CUT:
|
|
case IDM_COPY:
|
|
case IDM_PASTE:
|
|
case IDM_PASTEINSERT:
|
|
case IDM_DELETE:
|
|
case IDM_DELETEWORD:
|
|
case IDM_SELECTALL:
|
|
case IDM_UNDO:
|
|
case IDM_REDO:
|
|
case IDM_CHANGECASE:
|
|
case IDM_NONBREAK:
|
|
return S_FALSE;
|
|
}
|
|
|
|
// commands only available in HTML edit mode
|
|
if (!m_fPlainMode)
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case IDM_BOLD:
|
|
case IDM_UNDERLINE:
|
|
case IDM_ITALIC:
|
|
case IDM_REMOVEFORMAT:
|
|
case IDM_CENTERALIGNPARA:
|
|
case IDM_LEFTALIGNPARA:
|
|
case IDM_RIGHTALIGNPARA:
|
|
case IDM_REMOVEPARAFORMAT:
|
|
case IDM_APPLYNORMAL:
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (nCmdID)
|
|
m_fIgnoreAccel = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetOptionKeyPath
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetOptionKeyPath(LPOLESTR * pstrKey, DWORD dw)
|
|
{
|
|
HRESULT hr;
|
|
TCHAR rgch[MAX_PATH];
|
|
|
|
TraceCall("CBody::GetOptionKeyPath");
|
|
|
|
if (m_pParentDocHostUI)
|
|
{
|
|
// see if parent dochostUIhandler want's to handle. If not they will return _DODEFAULT
|
|
hr = m_pParentDocHostUI->GetOptionKeyPath(pstrKey, dw);
|
|
if (hr==S_OK)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, (WCHAR*)*pstrKey, -1, rgch, ARRAYSIZE(rgch), NULL, NULL);
|
|
CreateFontCache(rgch);
|
|
}
|
|
|
|
if (hr != MIMEEDIT_E_DODEFAULT)
|
|
return hr;
|
|
}
|
|
|
|
CreateFontCache(NULL);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: EnsureLoaded
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::EnsureLoaded()
|
|
{
|
|
HRESULT hr=NOERROR;
|
|
|
|
TraceCall("CBody::EnsureLoaded");
|
|
|
|
if (!m_lpOleObj)
|
|
{
|
|
hr=CDocHost::CreateDocObj((LPCLSID)&CLSID_HTMLDocument);
|
|
if(FAILED(hr))
|
|
goto error;
|
|
|
|
hr = CDocHost::Show();
|
|
if(FAILED(hr))
|
|
goto error;
|
|
|
|
Assert (!m_pDoc);
|
|
|
|
hr = m_lpOleObj->QueryInterface(IID_IHTMLDocument2, (LPVOID *)&m_pDoc);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = RegisterLoadNotify(TRUE);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (m_pFmtBar)
|
|
m_pFmtBar->SetCommandTarget(m_pCmdTarget);
|
|
}
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: RegisterLoadNotify
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::RegisterLoadNotify(BOOL fRegister)
|
|
{
|
|
IConnectionPointContainer *pCPContainer;
|
|
IConnectionPoint *pCP;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
TraceCall("CBody::RegisterLoadNotify");
|
|
|
|
if (!m_pDoc)
|
|
return E_FAIL;
|
|
|
|
if (m_pDoc)
|
|
{
|
|
hr = m_pDoc->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pCPContainer);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pCPContainer->FindConnectionPoint(IID_IPropertyNotifySink, &pCP);
|
|
if (!FAILED(hr))
|
|
{
|
|
if (fRegister)
|
|
{
|
|
Assert(m_dwNotify == 0);
|
|
hr = pCP->Advise((IPropertyNotifySink *)this, &m_dwNotify);
|
|
}
|
|
else
|
|
{
|
|
if (m_dwNotify)
|
|
{
|
|
hr = pCP->Unadvise(m_dwNotify);
|
|
m_dwNotify=0;
|
|
}
|
|
}
|
|
pCP->Release();
|
|
}
|
|
pCPContainer->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnChanged
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnChanged(DISPID dispid)
|
|
{
|
|
TraceCall("CBody::OnChanged");
|
|
|
|
if (dispid == DISPID_READYSTATE)
|
|
OnReadyStateChanged();
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnRequestEdit
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnRequestEdit (DISPID dispid)
|
|
{
|
|
TraceCall("CBody::OnRequestEdit");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnReadyStateChanged
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
void CBody::OnReadyStateChanged()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
VARIANT Var;
|
|
IDispatch * pdisp;
|
|
|
|
TraceCall("CBody::OnReadyStateChanged");
|
|
|
|
if (NULL == m_lpOleObj)
|
|
return;
|
|
|
|
if (m_lpOleObj->QueryInterface(IID_IDispatch, (void **)&pdisp)==S_OK)
|
|
{
|
|
if (GetDispProp(pdisp, DISPID_READYSTATE, 0, &Var, NULL)==S_OK)
|
|
{
|
|
// maybe either I4 or I2
|
|
Assert (Var.vt == VT_I4 || Var.vt == VT_I2);
|
|
// we get the ready state so we can warn about sending while downloading
|
|
m_dwReadyState = Var.lVal;
|
|
}
|
|
pdisp->Release();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnDocumentReady
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
void CBody::OnDocumentReady()
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
// quick-scan for CID's not requested before ParseComplete
|
|
if (m_fLoading && !m_fReloadingSrc)
|
|
SearchForCIDUrls();
|
|
|
|
// send a notification up to the parent
|
|
if (m_pParentCmdTarget)
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_ONPARSECOMPLETE, 0, NULL, NULL);
|
|
|
|
if (m_fAutoDetect)
|
|
StartAutoDetect();
|
|
|
|
#ifdef PLUSPACK
|
|
// Test for new Trident backgound spell check
|
|
if(m_fDesignMode && !m_pBkgSpeller && m_pDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHTMLElement * pElement = NULL;
|
|
IHTMLBodyElement * pBodyElement = NULL;
|
|
IHTMLTxtRange * pRange = NULL;
|
|
|
|
//
|
|
// Create the speller
|
|
//
|
|
CHECKHR(hr = CoCreateInstance(CLSID_HTMLSpell, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IHTMLSpell, (LPVOID*)&m_pBkgSpeller));
|
|
|
|
//
|
|
// Attach the speller
|
|
//
|
|
|
|
CHECKHR(hr = m_pDoc->get_body(&pElement));
|
|
|
|
CHECKHR(hr = m_pBkgSpeller->SetDoc(m_pDoc) );
|
|
CHECKHR(hr = m_pBkgSpeller->Attach(pElement) );
|
|
|
|
//
|
|
// Mark current content as clean
|
|
//
|
|
|
|
CHECKHR(hr = pElement->QueryInterface(IID_IHTMLBodyElement, (LPVOID *)&pBodyElement) );
|
|
CHECKHR(hr = pBodyElement->createTextRange(&pRange) );
|
|
CHECKHR(hr = m_pBkgSpeller->MarkRegion((IDispatch *)pRange, TRUE /* fSpellable */) );
|
|
|
|
exit:
|
|
// just cannot load background speller...
|
|
if(FAILED(hr))
|
|
Assert(FALSE);
|
|
|
|
SafeRelease(pRange);
|
|
SafeRelease(pBodyElement);
|
|
SafeRelease(pElement);
|
|
// return; //(S_OK);
|
|
}
|
|
#endif //PLUSPACK
|
|
|
|
#ifdef BACKGROUNDSPELL
|
|
HrCreateSpeller(TRUE);
|
|
#endif // BACKGROUNDSPELL
|
|
|
|
if (m_fLoading && !m_fReloadingSrc)
|
|
{
|
|
// OnDocumentReady can be called >1 times during a load. We use m_fLoading to keep track so
|
|
// we only do this init once.
|
|
|
|
// paste in the reply header and auto-text on the first download notification.
|
|
if (m_fDesignMode)
|
|
{
|
|
if (m_fForceCharsetLoad)
|
|
{
|
|
TCHAR rgchCset[CCHMAX_CSET_NAME];
|
|
|
|
if (SUCCEEDED(HrGetMetaTagName(m_hCharset, rgchCset, ARRAYSIZE(rgchCset))))
|
|
{
|
|
BSTR bstr;
|
|
if (SUCCEEDED(HrLPSZToBSTR(rgchCset, &bstr)))
|
|
{
|
|
m_pDoc->put_charset(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the host wants to to not send images that originate from an external source
|
|
// then tag them as such now.
|
|
if (GetHostFlags(&dwFlags)==S_OK &&
|
|
!(dwFlags & MEO_FLAGS_SENDEXTERNALIMGSRC))
|
|
{
|
|
SafeRelease(m_pHashExternal);
|
|
HashExternalReferences(m_pDoc, m_pMsg, &m_pHashExternal);
|
|
}
|
|
|
|
if (!m_fPlainMode)
|
|
{
|
|
SetHostComposeFont();
|
|
SetWindowBgColor(FALSE);
|
|
}
|
|
}
|
|
|
|
PasteReplyHeader();
|
|
PasteAutoText();
|
|
|
|
ClearUndoStack();
|
|
ClearDirtyFlag();
|
|
|
|
// if we get a load notify and we're the previewpane, show the attachment clip
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW)
|
|
{
|
|
UpdateBtnBar();
|
|
UpdatePreviewLabels();
|
|
}
|
|
m_fLoading=0;
|
|
}
|
|
|
|
m_fMessageParsed=1;
|
|
m_fReloadingSrc=FALSE;
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: SetRect
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::SetRect(LPRECT prc)
|
|
{
|
|
TraceCall("CBody::SetRect");
|
|
|
|
SetWindowPos(m_hwnd, NULL, prc->left, prc->top, prc->right-prc->left, prc->bottom-prc->top, SWP_NOZORDER);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetRect
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetRect(LPRECT prcView)
|
|
{
|
|
TraceCall("CBody::GetRect");
|
|
|
|
if (prcView == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
Assert (IsWindow(m_hwnd));
|
|
|
|
GetClientRect(m_hwnd, prcView);
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: UIActivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::UIActivate(BOOL fUIActivate)
|
|
{
|
|
TraceCall("CBody::UIActivate");
|
|
|
|
if(!m_pDocView)
|
|
return S_OK;
|
|
|
|
return m_pDocView->UIActivate(fUIActivate);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Exec
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
|
|
{
|
|
TraceCall("CBody::Exec");
|
|
|
|
if (pguidCmdGroup &&
|
|
IsEqualGUID(CMDSETID_Forms3, *pguidCmdGroup))
|
|
{
|
|
if (nCmdID == IDM_PARSECOMPLETE)
|
|
{
|
|
OnDocumentReady();
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
if (pguidCmdGroup==NULL)
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case OLECMDID_UPDATECOMMANDS:
|
|
UpdateCommands();
|
|
break;
|
|
}
|
|
}
|
|
return CDocHost::Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: GetClassID
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetClassID(CLSID *pCLSID)
|
|
{
|
|
TraceCall("CBody::GetClassID");
|
|
|
|
*pCLSID = CLSID_MimeEdit;
|
|
return NOERROR;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Load
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Load(IMimeMessage *pMsg)
|
|
{
|
|
BODYINITDATA biData;
|
|
|
|
TraceCall("CBody::Load");
|
|
|
|
if (pMsg == NULL)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
biData.dwType = BI_MESSAGE;
|
|
biData.pMsg = pMsg;
|
|
|
|
return LoadFromData(&biData);
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: Save
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Save(IMimeMessage *pMsg, DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwMHTMLFlags=0,
|
|
dwHostFlags;
|
|
|
|
TraceCall("CBody::Save");
|
|
|
|
if (pMsg==NULL)
|
|
return E_INVALIDARG;
|
|
|
|
if (!m_lpOleObj)
|
|
return CO_E_NOT_SUPPORTED;
|
|
|
|
|
|
if (m_uSrcView != MEST_EDIT)
|
|
return TraceResult(MIMEEDIT_E_CANNOTSAVEWHILESOURCEEDITING);
|
|
|
|
if (!((dwFlags & PMS_HTML) || (dwFlags & PMS_TEXT)))
|
|
return TraceResult(MIMEEDIT_E_ILLEGALBODYFORMAT);
|
|
|
|
// if trident is not yet done parsing the HTML, we cannot safely save from the tree.
|
|
if (!m_fMessageParsed)
|
|
return MIMEEDIT_E_CANNOTSAVEUNTILPARSECOMPLETE;
|
|
|
|
if(dwFlags & PMS_HTML)
|
|
dwMHTMLFlags |= MECD_HTML;
|
|
|
|
if(dwFlags & PMS_TEXT)
|
|
dwMHTMLFlags |= MECD_PLAINTEXT;
|
|
|
|
if ((GetHostFlags(&dwHostFlags)==S_OK) &&
|
|
(dwHostFlags & MEO_FLAGS_SENDIMAGES))
|
|
dwMHTMLFlags |= MECD_ENCODEIMAGES|MECD_ENCODESOUNDS|MECD_ENCODEVIDEO|MECD_ENCODEPLUGINS;
|
|
|
|
// turn off sound-playing during a save
|
|
EnableSounds(FALSE);
|
|
hr = SaveAsMHTML(m_pDoc, dwMHTMLFlags, m_pMsg, pMsg, m_pHashExternal);
|
|
EnableSounds(TRUE);
|
|
// if save was sucessful, but we haven't yet had a readystate complete notify then
|
|
// bubble a warning backup to give the user a chance to cancel.
|
|
if (hr==S_OK)
|
|
{
|
|
if (m_dwReadyState != READYSTATE_COMPLETE)
|
|
hr = MIMEEDIT_W_DOWNLOADNOTCOMPLETE;
|
|
else
|
|
ClearDirtyFlag(); // clear dirty flag if OK with no warnings.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::Load(BOOL fFullyAvailable, IMoniker *pMoniker, IBindCtx *pBindCtx, DWORD grfMode)
|
|
{
|
|
IMimeMessage *pMsg=0;
|
|
IPersistMoniker *pMsgMon=0;
|
|
HRESULT hr;
|
|
BODYINITDATA biData;
|
|
LPWSTR pszUrlW=0;
|
|
|
|
if (!pMoniker)
|
|
return E_INVALIDARG;
|
|
|
|
if (pMoniker->GetDisplayName(NULL, NULL, &pszUrlW)==S_OK &&
|
|
HrSniffUrlForRfc822(pszUrlW)==S_OK)
|
|
{
|
|
// if its a message moniker, load via IMimeMessage
|
|
hr = CoCreateInstance(CLSID_IMimeMessage, NULL, CLSCTX_INPROC_SERVER, IID_IMimeMessage, (LPVOID *)&pMsg);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = pMsg->QueryInterface(IID_IPersistMoniker, (LPVOID*)&pMsgMon);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = pMsgMon->Load(fFullyAvailable, pMoniker, pBindCtx, grfMode);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
biData.dwType = BI_MESSAGE;
|
|
biData.pMsg = pMsg;
|
|
}
|
|
else
|
|
{
|
|
biData.dwType = BI_MONIKER;
|
|
biData.pmk = pMoniker;
|
|
}
|
|
|
|
hr = LoadFromData(&biData);
|
|
|
|
error:
|
|
SafeRelease(pMsg);
|
|
SafeRelease(pMsgMon);
|
|
SafeMemFree(pszUrlW);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: IsDirty
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::IsDirty()
|
|
{
|
|
IPersistStreamInit *ppsi;
|
|
HRESULT hr=S_FALSE;
|
|
|
|
TraceCall("CBody::IsDirty");
|
|
|
|
// if we're not on the source-tab, use the old dirty-state
|
|
// if we're on richedit, make sure to see if source changes there
|
|
switch (m_uSrcView)
|
|
{
|
|
case MEST_SOURCE:
|
|
Assert (m_pSrcView);
|
|
return m_pSrcView->IsDirty();
|
|
|
|
case MEST_PREVIEW:
|
|
return m_fWasDirty ?S_OK:S_FALSE;
|
|
}
|
|
|
|
if (m_lpOleObj &&
|
|
m_lpOleObj->QueryInterface(IID_IPersistStreamInit, (LPVOID *)&ppsi)==S_OK)
|
|
{
|
|
hr = ppsi->IsDirty();
|
|
ppsi->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: LoadStream
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::LoadStream(LPSTREAM pstm)
|
|
{
|
|
HRESULT hr;
|
|
BODYINITDATA biData;
|
|
IMimeMessage *pMsg;
|
|
|
|
TraceCall("CBody::LoadStream");
|
|
|
|
if (pstm == NULL)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
// convert RFC822 stream into message
|
|
hr = MimeOleCreateMessage(NULL, &pMsg);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pMsg->Load(pstm);
|
|
if (!FAILED(hr))
|
|
{
|
|
biData.dwType = BI_MESSAGE;
|
|
biData.pMsg = pMsg;
|
|
hr = LoadFromData(&biData);
|
|
}
|
|
pMsg->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: LoadFromData
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::LoadFromData(LPBODYINITDATA pbiData)
|
|
{
|
|
HRESULT hr;
|
|
LPMONIKER pmk=0;
|
|
IMimeMessage *pMsg;
|
|
DWORD dwMsgFlags;
|
|
|
|
TraceCall("CBody::LoadFromData");
|
|
|
|
AssertSz(pbiData &&
|
|
( (pbiData->dwType == BI_MESSAGE && pbiData->pMsg) ||
|
|
(pbiData->dwType == BI_MONIKER && pbiData->pmk)),
|
|
"Caller should be validating params");
|
|
|
|
Assert (m_lpOleObj);
|
|
|
|
// make sure we're unloaded in case of failure
|
|
UnloadAll();
|
|
|
|
switch (pbiData->dwType)
|
|
{
|
|
case BI_MESSAGE:
|
|
{
|
|
WEBPAGEOPTIONS rOptions;
|
|
BOOL fIncludeMsg;
|
|
BOOL fDisplayingHtmlHelp = FALSE;
|
|
BOOL fGotHtmlHelpCharset = FALSE;
|
|
VARIANTARG va;
|
|
|
|
pMsg = pbiData->pMsg;
|
|
Assert(pMsg);
|
|
|
|
if(SUCCEEDED(m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_HTML_HELP, 0, NULL, &va)))
|
|
fDisplayingHtmlHelp = (va.boolVal == VARIANT_TRUE);
|
|
|
|
if(fDisplayingHtmlHelp)
|
|
{
|
|
IStream *pStream=NULL;
|
|
LPSTR pszCharset=NULL;
|
|
|
|
if(SUCCEEDED(pMsg->GetTextBody(TXT_HTML, IET_INETCSET, &pStream, NULL)))
|
|
{
|
|
if(SUCCEEDED(GetHtmlCharset(pStream, &pszCharset)))
|
|
{
|
|
if(SUCCEEDED(MimeOleFindCharset(pszCharset, &m_hCharset)))
|
|
fGotHtmlHelpCharset = TRUE;
|
|
MemFree(pszCharset);
|
|
}
|
|
|
|
pStream->Release();
|
|
}
|
|
}
|
|
|
|
if(!fGotHtmlHelpCharset)
|
|
{
|
|
// set the character set for use by the preview pane, painting etc
|
|
IF_FAILEXIT(hr = pMsg->GetCharset(&m_hCharset));
|
|
}
|
|
|
|
Assert(m_hCharset);
|
|
|
|
GetWebPageOptions(&rOptions, &fIncludeMsg);
|
|
|
|
if (m_fReloadingSrc || fIncludeMsg)
|
|
{
|
|
// only do autodetection if a plain-text message. this will occur if the
|
|
// message is plain-text only or the host is denying HTML.
|
|
if ( (pMsg->GetFlags(&dwMsgFlags)==S_OK) &&
|
|
(!(dwMsgFlags & IMF_HTML) || !(rOptions.dwFlags & WPF_HTML)))
|
|
m_fAutoDetect = 1;
|
|
|
|
// add NOMETACHASET as we setup a HTML load options now
|
|
rOptions.dwFlags |= WPF_NOMETACHARSET;
|
|
|
|
IF_FAILEXIT(hr = pMsg->CreateWebPage(NULL, &rOptions, 0, &pmk));
|
|
|
|
pmk->GetDisplayName(NULL, NULL, &m_pszUrlW);
|
|
|
|
IF_FAILEXIT(hr = LoadFromMoniker(pmk, m_hCharset));
|
|
}
|
|
else
|
|
{
|
|
m_fForceCharsetLoad = TRUE;
|
|
}
|
|
|
|
IF_FAILEXIT(hr = pMsg->QueryInterface(IID_IMimeMessageW, (LPVOID*)&m_pMsgW));
|
|
ReplaceInterface(m_pMsg, pMsg);
|
|
break;
|
|
}
|
|
|
|
case BI_MONIKER:
|
|
IF_FAILEXIT(hr = LoadFromMoniker(pbiData->pmk, NULL));
|
|
|
|
pbiData->pmk->GetDisplayName(NULL, NULL, &m_pszUrlW);
|
|
break;
|
|
}
|
|
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW)
|
|
UpdatePreviewLabels();
|
|
|
|
m_fEmpty=0;
|
|
|
|
exit:
|
|
ReleaseObj(pmk);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CreateBodyObject
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CreateBodyObject(HWND hwnd, DWORD dwFlags, LPRECT prc, PBODYHOSTINFO pHostInfo, LPBODYOBJ *ppBodyObj)
|
|
{
|
|
CBody *pBody;
|
|
HRESULT hr;
|
|
|
|
TraceCall("CreateBodyObject");
|
|
|
|
pBody = new CBody();
|
|
if (!pBody)
|
|
return TraceResult(E_OUTOFMEMORY);
|
|
|
|
hr = pBody->Init(hwnd, dwFlags, prc, pHostInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceResult(hr);
|
|
goto error;
|
|
}
|
|
|
|
*ppBodyObj = pBody;
|
|
pBody->AddRef();
|
|
|
|
error:
|
|
pBody->Release();
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: UrlHighlight
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::UrlHighlight(IHTMLTxtRange *pRange)
|
|
{
|
|
IOleCommandTarget *pCmdTarget=NULL;
|
|
VARIANT_BOOL boolVal;
|
|
|
|
TraceCall("UrlHighlight");
|
|
|
|
if (!pRange)
|
|
return E_INVALIDARG;
|
|
|
|
// calling IDM_AUTODETECT will mark the tree dirty, this is bad for replying to plaintext
|
|
// messages etc. To fix this we preserve the dirty between calls.
|
|
boolVal = IsDirty()==S_OK?VARIANT_TRUE:VARIANT_FALSE;
|
|
if (pRange->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&pCmdTarget)==S_OK)
|
|
{
|
|
pCmdTarget->Exec(&CMDSETID_Forms3, IDM_AUTODETECT, NULL, NULL, NULL);
|
|
pCmdTarget->Release();
|
|
HrSetDirtyFlagImpl(m_pDoc, boolVal);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::StartAutoDetect()
|
|
{
|
|
m_cchTotal=0;
|
|
m_cchStart=0;
|
|
SetTimer(m_hwnd, idTimerAutoDetect, AUTODETECT_TICKTIME, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::AutoDetectTimer()
|
|
{
|
|
LONG cch=0;
|
|
IMimeBody *pBody;
|
|
IMarkupPointer *pEndPtr=NULL;
|
|
DWORD dwPercent;
|
|
WCHAR wsz[CCHMAX_STRINGRES],
|
|
wszFmt[CCHMAX_STRINGRES],
|
|
wsz2[CCHMAX_STRINGRES];
|
|
LPWSTR pszW;
|
|
HBODY hBody;
|
|
HRESULT hr;
|
|
|
|
TraceCall("AutoDetectTimer");
|
|
|
|
// if mouse has capture, then ignore this timer-tick as user might be scrolling etc.
|
|
if (GetCapture())
|
|
return S_OK;
|
|
|
|
// turn of the timer, in case it takes a while
|
|
KillTimer(m_hwnd, idTimerAutoDetect);
|
|
|
|
// we try and give a rough %age estimate if autodetect is taking forever
|
|
// we get the total size of the plain-stream and divide the auto detect
|
|
// chunk into it. If we overflow, we sit at 100% for a while. Also, we don't show
|
|
// a %age until we've hit at least 2 auto detect timers (ie. a big document)
|
|
if (m_cchStart == AUTODETECT_CHUNK * 2)
|
|
{
|
|
// time to start showing progress.
|
|
|
|
Assert (m_cchTotal == 0);
|
|
if (m_pMsg->GetTextBody(TXT_PLAIN, IET_DECODED, NULL, &hBody)==S_OK)
|
|
{
|
|
if (m_pMsg->BindToObject(hBody, IID_IMimeBody, (LPVOID *)&pBody)==S_OK)
|
|
{
|
|
pBody->GetEstimatedSize(IET_BINARY, &m_cchTotal);
|
|
pBody->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
*wsz = 0;
|
|
LoadStringWrapW(g_hLocRes, idsSearchHLink, wsz, ARRAYSIZE(wsz));
|
|
|
|
if (m_cchTotal &&
|
|
LoadStringWrapW(g_hLocRes, idsSearchHLinkPC, wszFmt, ARRAYSIZE(wszFmt)))
|
|
{
|
|
dwPercent = ( m_cchStart * 100 / m_cchTotal );
|
|
if (dwPercent > 100)
|
|
dwPercent = 100;
|
|
wnsprintfW(wsz2, ARRAYSIZE(wsz2), wszFmt, dwPercent);
|
|
StrCatBuffW(wsz, wsz2, ARRAYSIZE(wsz));
|
|
}
|
|
|
|
SetStatusText(wsz);
|
|
|
|
m_fAutoDetect = 0;
|
|
|
|
if (m_pAutoStartPtr == NULL)
|
|
{
|
|
// create a starting pointer cache it incase we
|
|
// do chunking
|
|
hr = _CreateRangePointer(&m_pAutoStartPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
// create an end pointer
|
|
hr = _CreateRangePointer(&pEndPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// set the end poniter to the start pointer
|
|
hr = pEndPtr->MoveToPointer(m_pAutoStartPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// increment the end pointer by the amount to detect
|
|
cch = AUTODETECT_CHUNK;
|
|
hr = _MovePtrByCch(pEndPtr, &cch);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
m_cchStart += cch;
|
|
|
|
hr = _UrlHighlightBetweenPtrs(m_pAutoStartPtr, pEndPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// there must be more to detect, so set the timer
|
|
if (cch >= AUTODETECT_CHUNK)
|
|
{
|
|
m_fAutoDetect = 1;
|
|
|
|
// move the cached start pointer to the current end pointer
|
|
hr = m_pAutoStartPtr->MoveToPointer(pEndPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
|
|
if (m_fAutoDetect)
|
|
SetTimer(m_hwnd, idTimerAutoDetect, AUTODETECT_TICKTIME, NULL);
|
|
else
|
|
{
|
|
SetStatusText(NULL);
|
|
SafeRelease(m_pAutoStartPtr);
|
|
}
|
|
|
|
ReleaseObj(pEndPtr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::_MovePtrByCch(IMarkupPointer *pPtr, LONG *pcp)
|
|
{
|
|
HRESULT hr;
|
|
LONG cch,
|
|
cchDone=0;
|
|
MARKUP_CONTEXT_TYPE ctxt;
|
|
|
|
do
|
|
{
|
|
cch = *pcp - cchDone ;
|
|
|
|
hr = pPtr->Right(TRUE, &ctxt, NULL, &cch, NULL);
|
|
if (!FAILED(hr))
|
|
cchDone += cch;
|
|
}
|
|
while ( !FAILED(hr) &&
|
|
ctxt != CONTEXT_TYPE_None &&
|
|
cchDone < *pcp);
|
|
|
|
*pcp = cchDone;
|
|
return cchDone ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
HRESULT CBody::_CreateRangePointer(IMarkupPointer **ppPtr)
|
|
{
|
|
IMarkupServices *pMarkupServices=0;
|
|
IMarkupPointer *pPtr=0;
|
|
IHTMLBodyElement *pBodyElem=0;
|
|
IHTMLElement *pElem=0;
|
|
HRESULT hr;
|
|
|
|
if (ppPtr == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
*ppPtr = 0;
|
|
|
|
// get a markup services object
|
|
hr = m_pDoc->QueryInterface(IID_IMarkupServices, (void **) &pMarkupServices);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = pMarkupServices->CreateMarkupPointer(&pPtr);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// get the body element
|
|
hr = GetBodyElement(&pBodyElem);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// get right interface
|
|
hr = pBodyElem->QueryInterface(IID_IHTMLElement, (LPVOID *)&pElem);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// move start pointer to after begining of <BODY> tag
|
|
hr = pPtr->MoveAdjacentToElement(pElem, ELEM_ADJ_AfterBegin);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// hand back pointers
|
|
*ppPtr = pPtr;
|
|
pPtr = NULL;
|
|
|
|
error:
|
|
ReleaseObj(pElem);
|
|
ReleaseObj(pBodyElem);
|
|
ReleaseObj(pMarkupServices);
|
|
ReleaseObj(pPtr);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::_UrlHighlightBetweenPtrs(IMarkupPointer *pStartPtr, IMarkupPointer *pEndPtr)
|
|
{
|
|
IMarkupServices *pMarkupServices=0;
|
|
IHTMLTxtRange *pRange=0;
|
|
HRESULT hr;
|
|
|
|
// get a markup services object
|
|
hr = m_pDoc->QueryInterface(IID_IMarkupServices, (void **) &pMarkupServices);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// create a text range
|
|
hr = CreateRange(&pRange);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// use markup services to move pointers to range
|
|
hr = pMarkupServices->MoveRangeToPointers(pStartPtr, pEndPtr, pRange);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// autodetect range
|
|
hr = UrlHighlight(pRange);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
error:
|
|
ReleaseObj(pMarkupServices);
|
|
ReleaseObj(pRange);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: StopAutoDetect
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::StopAutoDetect()
|
|
{
|
|
TraceCall("CBody::StopAutoDetect");
|
|
|
|
if (m_fAutoDetect)
|
|
{
|
|
m_fAutoDetect = 0;
|
|
KillTimer(m_hwnd, idTimerAutoDetect);
|
|
TraceInfo("AutoDetect: CANCELED");
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: UnloadAll
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::UnloadAll()
|
|
{
|
|
TraceCall("CBody::UnloadAll");
|
|
|
|
StopAutoDetect();
|
|
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
|
|
if (!m_fEmpty)
|
|
{
|
|
if (m_lpOleObj)
|
|
HrInitNew(m_lpOleObj);
|
|
|
|
// if unloading due to a tab-switch in source-view
|
|
// don't blow away our restriction hash
|
|
if (!m_fReloadingSrc)
|
|
SafeRelease(m_pHashExternal);
|
|
|
|
SafeRelease(m_pAttMenu);
|
|
SafeRelease(m_pMsg);
|
|
SafeRelease(m_pMsgW);
|
|
SafeRelease(m_pRangeIgnoreSpell);
|
|
SafeRelease(m_pAutoStartPtr);
|
|
SafeCoTaskMemFree(m_pszUrlW);
|
|
SafeFreeTempFileList(m_pTempFileUrl);
|
|
|
|
SafeMimeOleFree(m_pszSubject);
|
|
SafeMimeOleFree(m_pszTo);
|
|
SafeMimeOleFree(m_pszCc);
|
|
SafeMimeOleFree(m_pszFrom);
|
|
|
|
// if unloading, clear button-bar
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW)
|
|
UpdateBtnBar();
|
|
|
|
m_dwReadyState = READYSTATE_UNINITIALIZED;
|
|
m_fMessageParsed = 0;
|
|
m_fEmpty = 1;
|
|
m_hCharset = NULL;
|
|
|
|
}
|
|
m_fLoading =1;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: LoadFromMoniker
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
HRESULT CBody::LoadFromMoniker(IMoniker *pmk, HCHARSET hCharset)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
LPPERSISTMONIKER pPMoniker=0;
|
|
LPBC pbc=0;
|
|
IHtmlLoadOptions *phlo;
|
|
DWORD uCodePage=0;
|
|
INETCSETINFO CsetInfo;
|
|
|
|
TraceCall("CBody::LoadFromMoniker");
|
|
|
|
Assert (m_lpOleObj);
|
|
|
|
hr=m_lpOleObj->QueryInterface(IID_IPersistMoniker, (LPVOID *)&pPMoniker);
|
|
if(FAILED(hr))
|
|
{
|
|
TraceResult(hr);
|
|
goto error;
|
|
}
|
|
|
|
hr=CreateBindCtx(0, &pbc);
|
|
if(FAILED(hr))
|
|
{
|
|
TraceResult(hr);
|
|
goto error;
|
|
}
|
|
|
|
if (hCharset)
|
|
{
|
|
// caller wants to override document charset
|
|
if (MimeOleGetCharsetInfo(hCharset, &CsetInfo)==S_OK)
|
|
uCodePage = CsetInfo.cpiInternet;
|
|
|
|
if (uCodePage &&
|
|
CoCreateInstance(CLSID_HTMLLoadOptions, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IHtmlLoadOptions, (void**)&phlo)==S_OK)
|
|
{
|
|
if (SUCCEEDED(phlo->SetOption(HTMLLOADOPTION_CODEPAGE,
|
|
&uCodePage, sizeof(uCodePage))))
|
|
pbc->RegisterObjectParam(L"__HTMLLOADOPTIONS", phlo);
|
|
phlo->Release();
|
|
}
|
|
}
|
|
|
|
hr=pPMoniker->Load(TRUE, pmk, pbc, STGM_READWRITE);
|
|
if(FAILED(hr))
|
|
{
|
|
TraceResult(hr);
|
|
goto error;
|
|
}
|
|
error:
|
|
ReleaseObj(pPMoniker);
|
|
ReleaseObj(pbc);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnFrameActivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
HRESULT CBody::OnFrameActivate(BOOL fActivate)
|
|
{
|
|
TraceCall("CBody::OnFrameActivate");
|
|
|
|
if (m_pInPlaceActiveObj)
|
|
m_pInPlaceActiveObj->OnFrameWindowActivate(fActivate);
|
|
|
|
// enable/disable sounds on frame activation
|
|
EnableSounds(fActivate);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL CBody::IsEmpty()
|
|
{
|
|
BOOL fEmpty = FALSE;
|
|
if (!m_fDesignMode)
|
|
{
|
|
if (m_fEmpty)
|
|
fEmpty = TRUE;
|
|
}
|
|
|
|
/*
|
|
** We need to come up with a good way to test if the text is
|
|
** empty. The test below always returns FALSE because of an nbsp
|
|
** that is in the text. If a better algorithm can be found, then
|
|
** we can reimplement this. The case that will hit this is posting
|
|
** a news note without any text. This should generate a TRUE.
|
|
else
|
|
{
|
|
IHTMLElement *pElem=0;
|
|
IHTMLTxtRange *pTxtRangeBegin = NULL,
|
|
*pTxtRangeEnd = NULL;
|
|
|
|
m_pDoc->get_body(&pElem);
|
|
if (pElem)
|
|
{
|
|
if (SUCCEEDED(CreateRange(&pTxtRangeBegin)))
|
|
{
|
|
pTxtRangeBegin->collapse(VARIANT_TRUE);
|
|
if (SUCCEEDED(CreateRange(&pTxtRangeEnd)))
|
|
{
|
|
pTxtRangeEnd->collapse(VARIANT_FALSE);
|
|
VARIANT_BOOL varBool = VARIANT_FALSE;
|
|
if (SUCCEEDED(pTxtRangeBegin->isEqual(pTxtRangeEnd, &varBool)) && (VARIANT_TRUE == varBool))
|
|
fEmpty = TRUE;
|
|
pTxtRangeEnd->Release();
|
|
}
|
|
pTxtRangeBegin->Release();
|
|
}
|
|
pElem->Release();
|
|
}
|
|
}
|
|
*/
|
|
return fEmpty;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::PrivateQueryStatus
|
|
//
|
|
// Synopsis: Private IOleCmdTarget called from the outer document
|
|
// to implement CDoc::CmdTarget
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::PrivateQueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
|
|
{
|
|
ULONG uCmd;
|
|
HRESULT hr=E_FAIL;
|
|
OLECMD *pCmd;
|
|
ULONG uCmdDisable;
|
|
static const UINT c_rgcmdIdNoPlain[] =
|
|
{
|
|
IDM_INDENT, IDM_OUTDENT, IDM_FONT, IDM_BLOCKFMT, IDM_IMAGE,
|
|
IDM_HORIZONTALLINE, IDM_UNLINK, IDM_HYPERLINK
|
|
};
|
|
|
|
|
|
|
|
TraceCall("CBody::PrivateQueryStatus");
|
|
|
|
if (m_uSrcView != MEST_EDIT &&
|
|
m_pSrcView &&
|
|
m_pSrcView->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText)==S_OK)
|
|
return S_OK;
|
|
|
|
if (pguidCmdGroup == NULL)
|
|
{
|
|
if (m_pCmdTarget)
|
|
hr = m_pCmdTarget->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
|
|
|
|
for (uCmd=0; uCmd < cCmds; uCmd++)
|
|
{
|
|
pCmd = &prgCmds[uCmd];
|
|
switch(pCmd->cmdID)
|
|
{
|
|
case OLECMDID_PRINT:
|
|
// if we have no m_pMsg, disable the print verb
|
|
if (!m_pMsg)
|
|
pCmd->cmdf=OLECMDF_SUPPORTED;
|
|
break;
|
|
|
|
case OLECMDID_FIND:
|
|
pCmd->cmdf=OLECMDF_ENABLED|OLECMDF_SUPPORTED;
|
|
hr = S_OK;
|
|
break;
|
|
|
|
case OLECMDID_SPELL:
|
|
pCmd->cmdf=OLECMDF_SUPPORTED;
|
|
|
|
if ((m_uSrcView == MEST_EDIT) && FCheckSpellAvail(m_pParentCmdTarget))
|
|
pCmd->cmdf |= OLECMDF_ENABLED;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
// delegate standard group to trident
|
|
}
|
|
else if (IsEqualGUID(*pguidCmdGroup, CMDSETID_Forms3))
|
|
{
|
|
// delegate Forms3 group to trident
|
|
if (m_pCmdTarget)
|
|
hr = m_pCmdTarget->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
|
|
|
|
// post-process triden't query status and disable commands that are not
|
|
// available in plain-text mode
|
|
if (m_fPlainMode)
|
|
{
|
|
for (uCmd=0; uCmd < cCmds; uCmd++)
|
|
{
|
|
for (uCmdDisable = 0; uCmdDisable < ARRAYSIZE(c_rgcmdIdNoPlain); uCmdDisable++)
|
|
{
|
|
if (c_rgcmdIdNoPlain[uCmdDisable] == prgCmds[uCmd].cmdID)
|
|
{
|
|
// if enabled, disable it
|
|
prgCmds[uCmd].cmdf &= ~OLECMDF_ENABLED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else if (IsEqualGUID(*pguidCmdGroup, CMDSETID_MimeEdit))
|
|
{
|
|
for (uCmd=0; uCmd < cCmds; uCmd++)
|
|
{
|
|
pCmd = &prgCmds[uCmd];
|
|
|
|
pCmd->cmdf = OLECMDF_SUPPORTED;
|
|
|
|
switch(pCmd->cmdID)
|
|
{
|
|
case MECMDID_TABLINKS:
|
|
case MECMDID_APPLYDOCUMENT:
|
|
case MECMDID_SAVEASSTATIONERY:
|
|
case MECMDID_DIRTY:
|
|
case MECMDID_INSERTTEXTFILE:
|
|
case MECMDID_FORMATFONT:
|
|
case MECMDID_SETTEXT:
|
|
case MECMDID_DOWNGRADEPLAINTEXT:
|
|
case MECMDID_CHARSET:
|
|
case MECMDID_ROT13:
|
|
case MECMDID_INSERTTEXT:
|
|
case MECMDID_INSERTHTML:
|
|
case MECMDID_BACKGROUNDCOLOR:
|
|
case MECMDID_STYLE:
|
|
case MECMDID_CANENCODETEXT:
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
break;
|
|
|
|
case MECMDID_SHOWSOURCETABS:
|
|
if (!m_fPlainMode)
|
|
{
|
|
// source-editing only available in plain-text mode
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
if (m_fSrcTabs)
|
|
pCmd->cmdf|=OLECMDF_LATCHED;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_EMPTY:
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
|
|
if (IsEmpty())
|
|
pCmd->cmdf|=OLECMDF_LATCHED;
|
|
break;
|
|
|
|
case MECMDID_EDITHTML:
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
|
|
if (!m_fPlainMode)
|
|
pCmd->cmdf|=OLECMDF_LATCHED;
|
|
break;
|
|
|
|
case MECMDID_EDITMODE:
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
|
|
if (m_fDesignMode)
|
|
pCmd->cmdf|=OLECMDF_LATCHED;
|
|
|
|
break;
|
|
|
|
case MECMDID_SAVEATTACHMENTS:
|
|
EnsureAttMenu();
|
|
if (m_pAttMenu &&
|
|
m_pAttMenu->HasEnabledAttach()==S_OK)
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
break;
|
|
|
|
case MECMDID_FORMATPARAGRAPH:
|
|
if (!m_fPlainMode)
|
|
pCmd->cmdf|=OLECMDF_ENABLED;
|
|
break;
|
|
|
|
default:
|
|
// not a recognised command
|
|
pCmd->cmdf = 0;
|
|
break;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = OLECMDERR_E_UNKNOWNGROUP;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::PrivateExec
|
|
//
|
|
// Synopsis: Private IOleCmdTarget called from the outer document
|
|
// to implement CDoc::CmdTarget
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::PrivateExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
|
|
{
|
|
HRESULT hr=E_FAIL;
|
|
|
|
TraceCall("CBody::PrivateExec");
|
|
|
|
if (m_uSrcView != MEST_EDIT &&
|
|
m_pSrcView &&
|
|
m_pSrcView->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut)==S_OK)
|
|
return S_OK;
|
|
|
|
if (pguidCmdGroup == NULL || IsEqualGUID(*pguidCmdGroup, CMDSETID_Forms3) || IsEqualGUID(*pguidCmdGroup, CGID_ShellDocView))
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case OLECMDID_SPELL:
|
|
{
|
|
VARIANTARG va;
|
|
// tell trident we are bringing up a modal dialog
|
|
// by calling PrivateEnableModeless
|
|
if (!m_pSpell) //no background spellchecking
|
|
HrCreateSpeller(FALSE);
|
|
|
|
if (m_pSpell)
|
|
{
|
|
PrivateEnableModeless(FALSE);
|
|
#ifdef BACKGROUNDSPELL
|
|
if (m_fBkgrndSpelling)
|
|
KillTimer(m_hwnd, idTimerBkgrndSpell);
|
|
#endif // BACKGROUNDSPELL
|
|
hr = m_pSpell->HrSpellChecking(m_pRangeIgnoreSpell, m_hwnd, (OLECMDEXECOPT_DONTPROMPTUSER==nCmdExecOpt)?TRUE:FALSE);
|
|
#ifdef BACKGROUNDSPELL
|
|
if (m_fBkgrndSpelling)
|
|
SetTimer(m_hwnd, idTimerBkgrndSpell, BKGRNDSPELL_TICKTIME, NULL);
|
|
#endif // BACKGROUNDSPELL
|
|
PrivateEnableModeless(TRUE);
|
|
}
|
|
}
|
|
return hr;
|
|
|
|
case OLECMDID_PRINT:
|
|
return Print(nCmdExecOpt!=OLECMDEXECOPT_DONTPROMPTUSER, pvaIn);
|
|
|
|
case OLECMDID_REFRESH:
|
|
return m_pCmdTarget ? m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_REFRESH, OLECMDEXECOPT_DODEFAULT, NULL, NULL) : E_FAIL;
|
|
|
|
case OLECMDID_FIND:
|
|
// Trident has own private Find, map OLE find to this.
|
|
return m_pCmdTarget ? m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_FIND, OLECMDEXECOPT_DODEFAULT, NULL, NULL) : E_FAIL;
|
|
}
|
|
|
|
// delegate standard commands and trident commandset we don't handle
|
|
// to trident
|
|
return m_pCmdTarget?m_pCmdTarget->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut):E_NOTIMPL;
|
|
}
|
|
else if (IsEqualGUID(*pguidCmdGroup, CMDSETID_MimeEdit))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
|
|
switch (nCmdID)
|
|
{
|
|
case MECMDID_SETSOURCETAB:
|
|
if (pvaIn && pvaIn->vt == VT_I4)
|
|
return SetSourceTabs(pvaIn->lVal);
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_I4;
|
|
pvaOut->lVal = m_fSrcTabs ? m_uSrcView : MEST_EDIT;
|
|
return S_OK;
|
|
}
|
|
return E_INVALIDARG;
|
|
|
|
case MECMDID_TABLINKS:
|
|
if (pvaIn && pvaIn->vt == VT_BOOL)
|
|
{
|
|
m_fTabLinks = (pvaIn->boolVal == VARIANT_TRUE);
|
|
return S_OK;
|
|
}
|
|
return E_INVALIDARG;
|
|
|
|
case MECMDID_SAVEATTACHMENTS:
|
|
return SaveAttachments();
|
|
|
|
case MECMDID_INSERTBGSOUND:
|
|
return InsertBackgroundSound();
|
|
|
|
case MECMDID_BACKGROUNDIMAGE:
|
|
if (pvaIn && pvaIn->vt == VT_BSTR)
|
|
return SetBackgroundImage(m_pDoc, pvaIn->bstrVal);
|
|
|
|
if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_BSTR;
|
|
return GetBackgroundImage(m_pDoc, &pvaOut->bstrVal);
|
|
}
|
|
|
|
return E_INVALIDARG;
|
|
|
|
case MECMDID_SHOWSOURCETABS:
|
|
if (pvaIn && pvaIn->vt == VT_BOOL)
|
|
{
|
|
ShowSourceTabs(pvaIn->boolVal == VARIANT_TRUE);
|
|
return S_OK;
|
|
}
|
|
return E_INVALIDARG;
|
|
|
|
case MECMDID_APPLYDOCUMENT:
|
|
return ApplyDocumentVerb(pvaIn);
|
|
|
|
case MECMDID_SAVEASSTATIONERY:
|
|
return SaveAsStationery(pvaIn, pvaOut);
|
|
|
|
case MECMDID_DIRTY:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_BOOL)
|
|
{
|
|
return HrSetDirtyFlagImpl(m_pDoc, pvaIn->boolVal==VARIANT_TRUE);
|
|
}
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_BOOL;
|
|
pvaOut->boolVal = IsDirty()==S_OK?VARIANT_TRUE:VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_FORMATPARAGRAPH:
|
|
return HrFormatParagraph();
|
|
break;
|
|
|
|
case MECMDID_EMPTY:
|
|
if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_BOOL;
|
|
pvaOut->boolVal = (IsEmpty() ? VARIANT_TRUE : VARIANT_FALSE);
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_DOWNGRADEPLAINTEXT:
|
|
return DowngradeToPlainText(pvaIn ? (pvaIn->vt == VT_BOOL ? (pvaIn->boolVal == VARIANT_TRUE) : NULL ): NULL );
|
|
|
|
case MECMDID_CHARSET:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_I8)
|
|
{
|
|
return SetCharset((HCHARSET)pvaIn->ullVal);
|
|
}
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_I8;
|
|
pvaOut->ullVal = (ULONGLONG)m_hCharset;
|
|
return S_OK;
|
|
}
|
|
|
|
break;
|
|
|
|
case MECMDID_FORMATFONT:
|
|
return FormatFont();
|
|
|
|
case MECMDID_PREVIEWFORMAT:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_BSTR)
|
|
{
|
|
SafeMemFree(m_pszLayout);
|
|
HrBSTRToLPSZ(CP_ACP, pvaIn->bstrVal, &m_pszLayout);
|
|
RecalcPreivewHeight(NULL);
|
|
Resize();
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
|
|
case MECMDID_INSERTTEXTFILE:
|
|
return InsertFile((pvaIn && pvaIn->vt==VT_BSTR) ? pvaIn->bstrVal : NULL);
|
|
|
|
case MECMDID_VIEWSOURCE:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_I4)
|
|
{
|
|
ViewSource(pvaIn->lVal == MECMD_VS_MESSAGE);
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_STYLE:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_I4)
|
|
return SetStyle(pvaIn->lVal);
|
|
|
|
if (pvaOut)
|
|
{
|
|
pvaOut->vt=VT_I4;
|
|
pvaOut->lVal = m_uHdrStyle;
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_ROT13:
|
|
if (pvaIn == NULL && pvaOut == NULL)
|
|
{
|
|
DoRot13();
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_INSERTTEXT:
|
|
case MECMDID_INSERTHTML:
|
|
if (pvaIn && pvaIn->vt == VT_BSTR)
|
|
{
|
|
return InsertTextAtCaret(pvaIn->bstrVal, MECMDID_INSERTHTML == nCmdID, FALSE);
|
|
}
|
|
break;
|
|
|
|
case MECMDID_BACKGROUNDCOLOR:
|
|
if (pvaIn && VT_I4 == pvaIn->vt)
|
|
return SetBackgroundColor(pvaIn->ulVal);
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_I4;
|
|
return GetBackgroundColor(&(pvaOut->ulVal));
|
|
}
|
|
break;
|
|
|
|
case MECMDID_EDITHTML:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_BOOL)
|
|
{
|
|
return SetPlainTextMode(pvaIn->boolVal!=VARIANT_TRUE);
|
|
}
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_BOOL;
|
|
pvaOut->boolVal = m_fPlainMode?VARIANT_FALSE:VARIANT_TRUE;
|
|
return S_OK;
|
|
}
|
|
break;
|
|
|
|
case MECMDID_EDITMODE:
|
|
if (pvaIn &&
|
|
pvaIn->vt == VT_BOOL)
|
|
{
|
|
return SetDesignMode(pvaIn->boolVal==VARIANT_TRUE);
|
|
}
|
|
else if (pvaOut)
|
|
{
|
|
pvaOut->vt = VT_BOOL;
|
|
pvaOut->boolVal = m_fDesignMode?VARIANT_TRUE:VARIANT_FALSE;
|
|
return S_OK;
|
|
}
|
|
return E_INVALIDARG;
|
|
|
|
case MECMDID_SETTEXT:
|
|
if (pvaIn==NULL || pvaIn->vt != VT_BSTR)
|
|
return E_INVALIDARG;
|
|
|
|
return SetDocumentText(pvaIn->bstrVal);
|
|
|
|
case MECMDID_CANENCODETEXT:
|
|
if (!pvaIn || pvaIn->vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
return SafeToEncodeText(pvaIn->lVal);
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT CBody::PrivateTranslateAccelerator(LPMSG lpmsg)
|
|
{
|
|
HRESULT hr=S_FALSE;
|
|
BOOL fTabbing=FALSE;
|
|
HWND hwndFocus;
|
|
|
|
// first of all, see if the formatbar is taking it...
|
|
if (m_fUIActive &&
|
|
m_uHdrStyle==MESTYLE_FORMATBAR &&
|
|
m_pFmtBar &&
|
|
m_pFmtBar->TranslateAcclerator(lpmsg)==S_OK)
|
|
return S_OK;
|
|
|
|
if (m_pSrcView &&
|
|
m_uSrcView == MEST_SOURCE)
|
|
{
|
|
return m_pSrcView->TranslateAccelerator(lpmsg);
|
|
}
|
|
|
|
// if trident is not in-place active, don't pass accelerators
|
|
if(!m_pInPlaceActiveObj)
|
|
return S_FALSE;
|
|
|
|
if (!m_fFocus)
|
|
{
|
|
// if trident doesn't have focus, make sure it's not a child
|
|
// of trident before blocking
|
|
hwndFocus = GetFocus();
|
|
|
|
if (hwndFocus == NULL ||
|
|
!IsChild(m_hwndDocObj, hwndFocus))
|
|
return S_FALSE;
|
|
}
|
|
|
|
// if Trident has focus and we get a TAB key in edit mode, we snag it and
|
|
// insert a tab ourselves
|
|
if (m_fFocus &&
|
|
lpmsg->message == WM_KEYDOWN &&
|
|
lpmsg->wParam == VK_TAB &&
|
|
m_fDesignMode &&
|
|
!(GetKeyState(VK_SHIFT)&0x8000))
|
|
{
|
|
// plain-tab with no shift in design mode inserts a tag
|
|
InsertTextAtCaret((BSTR)c_bstr_TabChar, FALSE, TRUE);
|
|
return S_OK;
|
|
}
|
|
|
|
if (lpmsg->message == WM_KEYDOWN)
|
|
{
|
|
if (lpmsg->wParam == VK_F6)
|
|
return S_FALSE;
|
|
|
|
if (lpmsg->wParam == VK_TAB)
|
|
{
|
|
if (!(m_fTabLinks))
|
|
return S_FALSE;
|
|
|
|
// if this control want's link-tabbing...
|
|
fTabbing=TRUE;
|
|
m_fCycleFocus=0;
|
|
}
|
|
}
|
|
|
|
// trident snags EVERYTHING. even non-keystrokes, only pass them keys...
|
|
if(lpmsg->message >= WM_KEYFIRST && lpmsg->message <= WM_KEYLAST)
|
|
{
|
|
hr=m_pInPlaceActiveObj->TranslateAccelerator(lpmsg);
|
|
|
|
if (m_fIgnoreAccel)
|
|
{
|
|
m_fIgnoreAccel = 0;
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (fTabbing && m_fCycleFocus)
|
|
return S_FALSE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::PrivateQueryService
|
|
//
|
|
// Synopsis: Private QueryService called from the outer document
|
|
// to implement CDoc::IServiceProvider
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::PrivateQueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::PrivateQueryService");
|
|
|
|
if (!m_pDoc)
|
|
return E_UNEXPECTED;
|
|
|
|
if (IsEqualGUID(guidService, IID_IHTMLDocument2) &&
|
|
IsEqualGUID(riid, IID_IHTMLDocument2))
|
|
{
|
|
*ppvObject = (LPVOID)m_pDoc;
|
|
m_pDoc->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
// RAID 12020. Needed to be able to get at DocHostUI for drop stuff
|
|
else if (IsEqualGUID(guidService, IID_IDocHostUIHandler) &&
|
|
IsEqualGUID(riid, IID_IDocHostUIHandler))
|
|
{
|
|
*ppvObject = (LPVOID)(IDocHostUIHandler*)this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::SetDesignMode
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::SetDesignMode(BOOL fOn)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::SetDesignMode");
|
|
|
|
if (fOn == m_fDesignMode) // bitfield!
|
|
return S_OK;
|
|
|
|
Assert (m_pCmdTarget);
|
|
Assert (m_lpOleObj);
|
|
|
|
hr = m_pCmdTarget->Exec(&CMDSETID_Forms3,
|
|
(fOn?IDM_EDITMODE:IDM_BROWSEMODE),
|
|
MSOCMDEXECOPT_DONTPROMPTUSER,
|
|
NULL, NULL);
|
|
if (!FAILED(hr))
|
|
{
|
|
m_fDesignMode = !!fOn; // bitfield
|
|
if (fOn)
|
|
{
|
|
SetHostComposeFont();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::SetPlainTextMode(BOOL fOn)
|
|
{
|
|
VARIANTARG va;
|
|
BSTR bstr=0;
|
|
|
|
m_fPlainMode=fOn;
|
|
|
|
va.bstrVal = NULL;
|
|
if (!fOn &&
|
|
m_pParentCmdTarget)
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_COMPOSE_FONT, OLECMDEXECOPT_DODEFAULT, NULL, &va);
|
|
|
|
SetComposeFont(va.bstrVal); //va.bstrVal could be NULL to turn compose font OFF
|
|
SysFreeString(va.bstrVal);
|
|
if (fOn)
|
|
{
|
|
// if going to plain-text mode, remove the tabs
|
|
ShowSourceTabs(FALSE);
|
|
}
|
|
else
|
|
{
|
|
// if going from plain-text > html, then exec a remove formatting command
|
|
// so that the new compose font is applied
|
|
IHTMLTxtRange *pTxtRange;
|
|
IOleCommandTarget *pCmdTarget;
|
|
|
|
if (!FAILED(CreateRange(&pTxtRange)))
|
|
{
|
|
if (!FAILED(pTxtRange->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&pCmdTarget)))
|
|
{
|
|
pCmdTarget->Exec(&CMDSETID_Forms3, IDM_REMOVEFORMAT, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
|
|
pCmdTarget->Release();
|
|
}
|
|
pTxtRange->Release();
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: DeleteElement
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::DeleteElement(IHTMLElement *pElem)
|
|
{
|
|
HRESULT hr=E_INVALIDARG;
|
|
|
|
TraceCall("CBody::DeleteElement");
|
|
if (pElem)
|
|
hr = pElem->put_outerHTML(NULL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: ReplaceElement
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::ReplaceElement(LPCTSTR pszName, BSTR bstrPaste, BOOL fHtml)
|
|
{
|
|
IHTMLElement *pElem;
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::HrReplaceElement");
|
|
|
|
if (!FAILED(hr = GetElement(pszName, &pElem)))
|
|
{
|
|
if (fHtml)
|
|
hr = pElem->put_outerHTML(bstrPaste);
|
|
else
|
|
hr = pElem->put_outerText(bstrPaste);
|
|
pElem->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: SelectElement
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::SelectElement(IHTMLElement *pElem, BOOL fScrollIntoView)
|
|
{
|
|
IHTMLTxtRange *pRange;
|
|
HRESULT hr;
|
|
VARIANT v;
|
|
|
|
TraceCall("CBody::SelectElement");
|
|
|
|
if (pElem == NULL)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
if (!FAILED(hr=CreateRangeFromElement(pElem, &pRange)))
|
|
{
|
|
pRange->collapse(VARIANT_TRUE);
|
|
pRange->select();
|
|
if (fScrollIntoView)
|
|
pRange->scrollIntoView(VARIANT_TRUE);
|
|
pRange->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CreateRangeFromElement
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::CreateRangeFromElement(IHTMLElement *pElem, IHTMLTxtRange **ppRange)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLTxtRange *pRange;
|
|
|
|
TraceCall("CBody::CreateRangeFromElement");
|
|
|
|
Assert (pElem && ppRange);
|
|
|
|
if (!FAILED(hr = CreateRange(&pRange)))
|
|
{
|
|
if (!FAILED(hr = pRange->moveToElementText(pElem)))
|
|
{
|
|
*ppRange = pRange;
|
|
pRange->AddRef();
|
|
}
|
|
pRange->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: HrCreateRange
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::CreateRange(IHTMLTxtRange **ppRange)
|
|
{
|
|
IHTMLBodyElement *pBodyElem=0;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
TraceCall("CBody::HrCreateRange");
|
|
|
|
Assert (ppRange);
|
|
*ppRange = NULL;
|
|
if (!FAILED(GetBodyElement(&pBodyElem)))
|
|
{
|
|
pBodyElem->createTextRange(ppRange);
|
|
if (*ppRange)
|
|
hr = S_OK;
|
|
pBodyElem->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::GetSelection
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetSelection(IHTMLTxtRange **ppRange)
|
|
{
|
|
IHTMLSelectionObject *pSel=0;
|
|
IHTMLTxtRange *pTxtRange=0;
|
|
IDispatch *pID=0;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
TraceCall("CBody::GetSelection");
|
|
|
|
if (ppRange == NULL)
|
|
return TraceResult(E_INVALIDARG);
|
|
|
|
*ppRange = NULL;
|
|
|
|
if(m_pDoc)
|
|
{
|
|
m_pDoc->get_selection(&pSel);
|
|
if (pSel)
|
|
{
|
|
pSel->createRange(&pID);
|
|
if (pID)
|
|
{
|
|
hr = pID->QueryInterface(IID_IHTMLTxtRange, (LPVOID *)ppRange);
|
|
pID->Release();
|
|
}
|
|
pSel->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::GetSelection
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetElement(LPCTSTR pszName, IHTMLElement **ppElem)
|
|
{
|
|
TraceCall("CBody::GetElement");
|
|
|
|
return ::HrGetElementImpl(m_pDoc, pszName, ppElem);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::GetSelection
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetBodyElement(IHTMLBodyElement **ppBody)
|
|
{
|
|
TraceCall("CBody::GetBodyElement");
|
|
|
|
return ::HrGetBodyElement(m_pDoc, ppBody);
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::Print
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::Print(BOOL fPrompt, VARIANTARG *pvaIn)
|
|
{
|
|
VARIANTARG va;
|
|
SAFEARRAY *psa = NULL;
|
|
SAFEARRAYBOUND rgsaBound[1];
|
|
LONG l;
|
|
BSTR bstrFooter = NULL,
|
|
bstrHeader = NULL,
|
|
bstrUrl = NULL;
|
|
HRESULT hr;
|
|
VARIANT v;
|
|
LPSTREAM pstm = NULL;
|
|
BOOL fMHtml = FALSE;
|
|
HBODY hBody;
|
|
WCHAR wsz[CCHMAX_STRINGRES];
|
|
LPWSTR pwszUser = NULL;
|
|
|
|
TraceCall("CBody::Print");
|
|
|
|
if (m_pCmdTarget==NULL)
|
|
return TraceResult(E_UNEXPECTED);
|
|
|
|
// if we're printing an MHTML message, print by URL so that cid:'s print
|
|
// NB: this is a loss of fidelity.
|
|
if (m_pMsg)
|
|
fMHtml = (MimeOleGetRelatedSection(m_pMsg, FALSE, &hBody, NULL)==S_OK) && m_pszUrlW;
|
|
|
|
rgsaBound[0].lLbound = 0;
|
|
rgsaBound[0].cElements = fMHtml ? 4 : 3;
|
|
|
|
IF_NULLEXIT(psa = SafeArrayCreate(VT_VARIANT, 1, rgsaBound));
|
|
|
|
va.vt = VT_ARRAY|VT_BYREF;
|
|
va.parray = psa;
|
|
|
|
*wsz = 0;
|
|
SideAssert(LoadStringWrapW(g_hLocRes, idsPrintHeader, wsz, ARRAYSIZE(wsz)));
|
|
|
|
IF_NULLEXIT(bstrHeader = SysAllocString(wsz));
|
|
|
|
v.vt = VT_BSTR;
|
|
v.bstrVal = bstrHeader;
|
|
|
|
// don't show the <TITLE> tag on the header
|
|
l=0;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, &v));
|
|
|
|
*wsz = 0;
|
|
SideAssert(LoadStringWrapW(g_hLocRes, idsPrintFooter, wsz, ARRAYSIZE(wsz)));
|
|
|
|
// we want footer to have only date, not URL.
|
|
IF_NULLEXIT(bstrFooter = SysAllocString(wsz));
|
|
|
|
v.vt = VT_BSTR;
|
|
v.bstrVal = bstrFooter;
|
|
|
|
// FOOTER
|
|
l=1;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, &v));
|
|
|
|
if (m_pMsg)
|
|
{
|
|
if (pvaIn && pvaIn->vt == VT_BSTR)
|
|
pwszUser = pvaIn->bstrVal;
|
|
|
|
IF_FAILEXIT(hr = GetHeaderTable(m_pMsgW, pwszUser, HDR_TABLE, &pstm));
|
|
|
|
HrRewindStream(pstm);
|
|
|
|
// ISTREAM containing table
|
|
v.vt = VT_UNKNOWN;
|
|
v.punkVal = (LPUNKNOWN)pstm;
|
|
|
|
l=2;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, &v));
|
|
|
|
if (fMHtml)
|
|
{
|
|
bstrUrl = SysAllocString(m_pszUrlW);
|
|
|
|
v.vt = VT_BSTR;
|
|
v.bstrVal = bstrUrl;
|
|
|
|
l=3;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, &v));
|
|
}
|
|
|
|
}
|
|
|
|
hr = m_pCmdTarget->Exec(NULL, OLECMDID_PRINT,
|
|
(fPrompt)?(OLECMDEXECOPT_PROMPTUSER):(OLECMDEXECOPT_DONTPROMPTUSER), &va, NULL);
|
|
IF_FAILEXIT(hr);
|
|
|
|
exit:
|
|
SysFreeString(bstrFooter);
|
|
SysFreeString(bstrHeader);
|
|
SysFreeString(bstrUrl);
|
|
if (psa)
|
|
SafeArrayDestroy(psa);
|
|
ReleaseObj(pstm);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::OnWMCommand
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnWMCommand(HWND hwnd, int id, WORD wCmd)
|
|
{
|
|
DWORD nCmdId=0,
|
|
nCmdIdTrident=0,
|
|
nCmdIdOESecure=0;
|
|
POINT pt;
|
|
VARIANTARG va;
|
|
VARIANTARG *pvaIn=0,
|
|
*pvaOut=0;
|
|
|
|
TraceCall("CBody::OnWMCommand");
|
|
|
|
if (m_pSrcView &&
|
|
m_pSrcView->OnWMCommand(hwnd, id, wCmd)==S_OK)
|
|
return S_OK;
|
|
|
|
if (m_pFmtBar &&
|
|
m_pFmtBar->OnWMCommand(hwnd, id, wCmd)==S_OK)
|
|
return S_OK;
|
|
|
|
if (!m_pCmdTarget)
|
|
return S_FALSE;
|
|
|
|
switch (id)
|
|
{
|
|
case idmPaneEncryption:
|
|
case idmPaneBadEncryption:
|
|
if (PointFromButton(id, &pt)==S_OK)
|
|
{
|
|
va.vt = VT_BYREF;
|
|
va.byref = &pt;
|
|
pvaIn = &va;
|
|
nCmdIdOESecure = OECSECCMD_ENCRYPTED;
|
|
}
|
|
break;
|
|
|
|
case idmPaneBadSigning:
|
|
case idmPaneSigning:
|
|
if (PointFromButton(id, &pt)==S_OK)
|
|
{
|
|
va.vt = VT_BYREF;
|
|
va.byref = &pt;
|
|
pvaIn = &va;
|
|
nCmdIdOESecure = OECSECCMD_SIGNED;
|
|
}
|
|
break;
|
|
|
|
case idmPaneVCard:
|
|
if (m_pAttMenu)
|
|
m_pAttMenu->LaunchVCard(m_hwnd);
|
|
break;
|
|
|
|
#ifdef FOLLOW_LINK
|
|
case idmOpenLink:
|
|
nCmdIdTrident = IDM_FOLLOWLINKN;
|
|
break;
|
|
#endif
|
|
|
|
case idmCopy:
|
|
nCmdId = OLECMDID_COPY;
|
|
break;
|
|
|
|
case idmSaveTargetAs:
|
|
nCmdIdTrident = IDM_SAVETARGET;
|
|
break;
|
|
|
|
case idmCopyShortcut:
|
|
nCmdIdTrident = IDM_COPYSHORTCUT;
|
|
break;
|
|
|
|
case idmCut:
|
|
nCmdId = OLECMDID_CUT;
|
|
break;
|
|
|
|
case idmPaste:
|
|
nCmdId = OLECMDID_PASTE;
|
|
break;
|
|
|
|
case idmSelectAll:
|
|
nCmdId = OLECMDID_SELECTALL;
|
|
break;
|
|
|
|
case idmUndo:
|
|
nCmdId = OLECMDID_UNDO;
|
|
break;
|
|
|
|
case idmRedo:
|
|
nCmdId = OLECMDID_REDO;
|
|
break;
|
|
|
|
case idmAddToFavorites:
|
|
AddToFavorites();
|
|
return S_OK;
|
|
|
|
case idmAddToWAB:
|
|
AddToWab();
|
|
return S_OK;
|
|
|
|
case idmSaveBackground:
|
|
nCmdIdTrident = IDM_SAVEBACKGROUND;
|
|
break;
|
|
|
|
case idmSavePicture:
|
|
_OnSaveImage();
|
|
break;
|
|
|
|
case idmFmtLeft:
|
|
nCmdIdTrident = IDM_JUSTIFYLEFT;
|
|
break;
|
|
|
|
case idmFmtCenter:
|
|
nCmdIdTrident = IDM_JUSTIFYCENTER;
|
|
break;
|
|
|
|
case idmFmtRight:
|
|
nCmdIdTrident = IDM_JUSTIFYRIGHT;
|
|
break;
|
|
|
|
case idmFmtNumbers:
|
|
nCmdIdTrident = IDM_ORDERLIST;
|
|
break;
|
|
|
|
case idmFmtBullets:
|
|
nCmdIdTrident = IDM_UNORDERLIST;
|
|
break;
|
|
|
|
case idmFmtBlockDirLTR:
|
|
nCmdIdTrident = IDM_BLOCKDIRLTR;
|
|
break;
|
|
|
|
case idmFmtBlockDirRTL:
|
|
nCmdIdTrident = IDM_BLOCKDIRRTL;
|
|
break;
|
|
|
|
case idmFmtFontDlg:
|
|
return FormatFont();
|
|
|
|
case idmFmtParagraphDlg:
|
|
return HrFormatParagraph();
|
|
|
|
case idmProperties:
|
|
nCmdIdTrident = DwChooseProperties();
|
|
if (nCmdIdTrident==0)
|
|
{
|
|
return DoHostProperties();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(nCmdId)
|
|
{
|
|
m_pCmdTarget->Exec(NULL, nCmdId, OLECMDEXECOPT_DODEFAULT, pvaIn, pvaOut);
|
|
return S_OK;
|
|
}
|
|
|
|
if(nCmdIdTrident)
|
|
{
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, nCmdIdTrident, OLECMDEXECOPT_DODEFAULT, pvaIn, pvaOut);
|
|
return S_OK;
|
|
}
|
|
|
|
if (nCmdIdOESecure && m_pParentCmdTarget)
|
|
{
|
|
// delegate security commands to the host
|
|
m_pParentCmdTarget->Exec(&CMDSETID_OESecurity, nCmdIdOESecure, OLECMDEXECOPT_DODEFAULT, pvaIn, pvaOut);
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::UpdateContextMenu
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::UpdateContextMenu(HMENU hmenuEdit, BOOL fEnableProperties, IDispatch *pDisp)
|
|
{
|
|
OLECMD rgTridentCmds[]= {{IDM_FIND, 0},
|
|
{IDM_SAVEBACKGROUND, 0},
|
|
{IDM_COPYSHORTCUT, 0},
|
|
{IDM_SAVETARGET, 0}};
|
|
OLECMD rgStdCmds[]= {{OLECMDID_CUT, 0},
|
|
{OLECMDID_COPY, 0},
|
|
{OLECMDID_PASTE, 0},
|
|
{OLECMDID_SELECTALL, 0},
|
|
{OLECMDID_UNDO, 0},
|
|
{OLECMDID_REDO, 0}};
|
|
int rgidsStd[]= {idmCut,
|
|
idmCopy,
|
|
idmPaste,
|
|
idmSelectAll,
|
|
idmUndo,
|
|
idmRedo};
|
|
int rgidsTrident[]= {idmFindText,
|
|
idmSaveBackground,
|
|
idmCopyShortcut,
|
|
idmSaveTargetAs};
|
|
OLECMD rgFmtCmds[]= {{IDM_INDENT, 0}, // careful about ordering!!
|
|
{IDM_OUTDENT, 0},
|
|
{IDM_FONT, 0}};
|
|
int rgFmtidm[] = {idmFmtIncreaseIndent, // careful about ordering!!
|
|
idmFmtDecreaseIndent,
|
|
idmFmtFontDlg};
|
|
OLECMD rgHostCmds[]= {{OLECMDID_PROPERTIES, 0}};
|
|
int rgHostidm[]= {idmProperties};
|
|
UINT ustate;
|
|
int i,
|
|
id;
|
|
BSTR bstrHref=NULL;
|
|
BOOL fBadLink = FALSE;
|
|
IHTMLAnchorElement *pAE;
|
|
|
|
TraceCall("CBody::UpdateContextMenu");
|
|
|
|
if (!m_pCmdTarget || !hmenuEdit)
|
|
return E_INVALIDARG;
|
|
|
|
if (pDisp &&
|
|
pDisp->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE)==S_OK)
|
|
{
|
|
pAE->get_href(&bstrHref);
|
|
if (bstrHref &&
|
|
StrCmpNIW(bstrHref, c_szOECmdW, ARRAYSIZE(c_szOECmdW)-sizeof(WCHAR))==0)
|
|
fBadLink = TRUE;
|
|
|
|
pAE->Release();
|
|
}
|
|
|
|
m_pCmdTarget->QueryStatus(NULL, sizeof(rgStdCmds)/sizeof(OLECMD), rgStdCmds, NULL);
|
|
for (i=0; i<sizeof(rgStdCmds)/sizeof(OLECMD); i++)
|
|
SetMenuItem(hmenuEdit, rgidsStd[i], rgStdCmds[i].cmdf & OLECMDF_ENABLED);
|
|
|
|
m_pCmdTarget->QueryStatus(&CMDSETID_Forms3, sizeof(rgTridentCmds)/sizeof(OLECMD), rgTridentCmds, NULL);
|
|
|
|
for (i=0; i<sizeof(rgTridentCmds)/sizeof(OLECMD); i++)
|
|
{
|
|
ustate = rgTridentCmds[i].cmdf & OLECMDF_ENABLED;
|
|
|
|
if (rgidsTrident[i] == idmSaveTargetAs || rgidsTrident[i] == idmCopyShortcut)
|
|
ustate = ustate && !fBadLink;
|
|
|
|
SetMenuItem(hmenuEdit, rgidsTrident[i], ustate);
|
|
}
|
|
|
|
// set the formatting commands. The are only available when we have focus and we are editing an HTML document
|
|
if (m_fFocus && !m_fPlainMode)
|
|
m_pCmdTarget->QueryStatus(&CMDSETID_Forms3, sizeof(rgFmtCmds)/sizeof(OLECMD), rgFmtCmds, NULL);
|
|
|
|
ustate = !m_fPlainMode ? MF_ENABLED : (MF_DISABLED|MF_GRAYED);
|
|
EnableMenuItem(hmenuEdit, idmFmtParagraphDlg, ustate);
|
|
|
|
for(i=0; i<sizeof(rgFmtCmds)/sizeof(OLECMD); i++)
|
|
{
|
|
ustate = (rgFmtCmds[i].cmdf&OLECMDF_ENABLED ? MF_ENABLED: MF_DISABLED|MF_GRAYED) | MF_BYCOMMAND;
|
|
EnableMenuItem(hmenuEdit, rgFmtidm[i], ustate);
|
|
ustate = (rgFmtCmds[i].cmdf&OLECMDF_LATCHED?MF_CHECKED:MF_UNCHECKED) | MF_BYCOMMAND;
|
|
CheckMenuItem(hmenuEdit, rgFmtidm[i], ustate);
|
|
}
|
|
|
|
if (m_pParentCmdTarget)
|
|
m_pParentCmdTarget->QueryStatus(NULL, sizeof(rgHostCmds)/sizeof(OLECMD), rgHostCmds, NULL);
|
|
|
|
// if we are handling idmProperties then override the host state
|
|
if (fEnableProperties)
|
|
rgHostCmds[0].cmdf = OLECMDF_ENABLED;
|
|
|
|
for(i=0; i<sizeof(rgHostCmds)/sizeof(OLECMD); i++)
|
|
SetMenuItem(hmenuEdit, rgHostidm[i], rgHostCmds[i].cmdf & OLECMDF_ENABLED);
|
|
|
|
SysFreeString(bstrHref);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::AppendAnchorItems
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::AppendAnchorItems(HMENU hMenu, IDispatch *pDisp)
|
|
{
|
|
BSTR bstr=0;
|
|
TCHAR rgch[CCHMAX_STRINGRES];
|
|
HRESULT hr=S_OK;
|
|
IHTMLAnchorElement *pAE;
|
|
OLECMD rgHostCmds[]={ {MEHOSTCMDID_ADD_TO_ADDRESSBOOK, 0},
|
|
{MEHOSTCMDID_ADD_TO_FAVORITES, 0}};
|
|
|
|
TraceCall("CBody::AppendAnchorItems");
|
|
|
|
if (!pDisp)
|
|
return S_OK;
|
|
|
|
// if no parent cmdtarget, can't do these verbs anyway
|
|
if (m_pParentCmdTarget &&
|
|
m_pParentCmdTarget->QueryStatus(&CMDSETID_MimeEditHost, sizeof(rgHostCmds)/sizeof(OLECMD), rgHostCmds, NULL)==S_OK)
|
|
{
|
|
if (pDisp->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE)==S_OK)
|
|
{
|
|
hr = pAE->get_href(&bstr);
|
|
pAE->Release();
|
|
}
|
|
|
|
if (bstr)
|
|
{
|
|
*rgch = 0;
|
|
|
|
// If "mailto:" is in bstr then add it to wab; If "http:" is in bstr, add it to favorites
|
|
if (rgHostCmds[0].cmdf & OLECMDF_ENABLED &&
|
|
StrCmpNIW(bstr, c_szMailToW, ARRAYSIZE(c_szMailToW)-sizeof(WCHAR))==0)
|
|
{
|
|
SideAssert(LoadString(g_hLocRes, idsAddToWAB, rgch, sizeof(rgch)/sizeof(TCHAR)));
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, idmAddToWAB, rgch);
|
|
RemoveMenu(hMenu, idmSaveTargetAs, MF_BYCOMMAND); // no point on a mailto: url
|
|
}
|
|
else if (rgHostCmds[1].cmdf & OLECMDF_ENABLED &&
|
|
(StrCmpNIW(bstr, c_szHttpW, ARRAYSIZE(c_szHttpW)-sizeof(WCHAR))==0 ||
|
|
StrCmpNIW(bstr, c_szFileW, ARRAYSIZE(c_szFileW)-sizeof(WCHAR))==0 ))
|
|
{
|
|
SideAssert(LoadString(g_hLocRes, idsAddToFavorites, rgch, sizeof(rgch)/sizeof(TCHAR)));
|
|
AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
|
|
AppendMenu(hMenu, MF_STRING, idmAddToFavorites, rgch);
|
|
}
|
|
|
|
SysFreeString(bstr);
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::GetSelectedAnchor
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetSelectedAnchor(BSTR* pbstr)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLTxtRange *pTxtRange;
|
|
IHTMLAnchorElement *pAE;
|
|
IHTMLElement *pElemParent;
|
|
|
|
TraceCall("CBody::GetSelectedAnchor");
|
|
|
|
if (pbstr)
|
|
*pbstr=NULL;
|
|
|
|
hr = GetSelection(&pTxtRange);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pTxtRange->parentElement(&pElemParent);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = pElemParent->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE);
|
|
if (!FAILED(hr))
|
|
{
|
|
if (pbstr)
|
|
hr = pAE->get_href(pbstr);
|
|
pAE->Release();
|
|
}
|
|
pElemParent->Release();
|
|
}
|
|
pTxtRange->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::AddToWab
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::AddToWab()
|
|
{
|
|
BSTR bstr=0,
|
|
bstrOut;
|
|
HRESULT hr=E_FAIL;
|
|
WCHAR *pszW;
|
|
VARIANTARG va;
|
|
IHTMLAnchorElement *pAE;
|
|
|
|
TraceCall("CBody::AddToWab");
|
|
|
|
if (!m_pParentCmdTarget)
|
|
return TraceResult(E_UNEXPECTED);
|
|
|
|
if (m_pDispContext &&
|
|
m_pDispContext->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE)==S_OK)
|
|
{
|
|
pAE->get_href(&bstr);
|
|
pAE->Release();
|
|
}
|
|
|
|
if (bstr)
|
|
{
|
|
// double check this is a mailto:
|
|
if (StrCmpNIW(bstr, c_szMailToW, ARRAYSIZE(c_szMailToW)-sizeof(WCHAR))==0)
|
|
{
|
|
pszW = bstr + (ARRAYSIZE(c_szMailToW)-1);
|
|
|
|
bstrOut = SysAllocString(pszW);
|
|
if (bstrOut)
|
|
{
|
|
va.vt = VT_BSTR;
|
|
va.bstrVal = bstrOut;
|
|
hr = m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_ADD_TO_ADDRESSBOOK, OLECMDEXECOPT_DODEFAULT, &va, NULL);
|
|
SysFreeString(bstrOut);
|
|
}
|
|
else
|
|
hr = TraceResult(E_OUTOFMEMORY);
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::AddToFavorites
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::AddToFavorites()
|
|
{
|
|
SAFEARRAY *psa=NULL;
|
|
SAFEARRAYBOUND rgsaBound[1];
|
|
BSTR bstrURL=NULL,
|
|
bstrDescr=NULL;
|
|
HRESULT hr=E_FAIL;
|
|
VARIANTARG va;
|
|
IHTMLAnchorElement *pAE=NULL;
|
|
IHTMLElement *pE=NULL;
|
|
LONG l;
|
|
|
|
TraceCall("CBody::AddToFavorites");
|
|
|
|
if (!m_pParentCmdTarget)
|
|
return TraceResult(E_UNEXPECTED);
|
|
|
|
if (m_pDispContext &&
|
|
m_pDispContext->QueryInterface(IID_IHTMLElement, (LPVOID *)&pE)==S_OK)
|
|
{
|
|
pE->get_innerText(&bstrDescr);
|
|
pE->Release();
|
|
}
|
|
|
|
if (m_pDispContext &&
|
|
m_pDispContext->QueryInterface(IID_IHTMLAnchorElement, (LPVOID *)&pAE)==S_OK)
|
|
{
|
|
pAE->get_href(&bstrURL);
|
|
pAE->Release();
|
|
}
|
|
|
|
if (bstrURL)
|
|
{
|
|
if(!bstrDescr)
|
|
bstrDescr=SysAllocString(bstrURL);
|
|
|
|
rgsaBound[0].lLbound = 0;
|
|
rgsaBound[0].cElements = 2;
|
|
|
|
IF_NULLEXIT(psa = SafeArrayCreate(VT_BSTR, 1, rgsaBound));
|
|
|
|
va.vt = VT_ARRAY|VT_BSTR;
|
|
va.parray = psa;
|
|
|
|
l=0;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, bstrDescr));
|
|
|
|
l=1;
|
|
IF_FAILEXIT(hr = SafeArrayPutElement(psa, &l, bstrURL));
|
|
|
|
IF_FAILEXIT(hr = m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_ADD_TO_FAVORITES, OLECMDEXECOPT_DODEFAULT, &va, NULL));
|
|
}
|
|
|
|
exit:
|
|
SysFreeString(bstrDescr);
|
|
SysFreeString(bstrURL);
|
|
if(psa)
|
|
SafeArrayDestroy(psa);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: CBody::GetWebPageOptions
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::GetWebPageOptions(WEBPAGEOPTIONS *pOptions, BOOL *pfIncludeMsg)
|
|
{
|
|
VARIANTARG va;
|
|
DWORD dwFlags;
|
|
|
|
TraceCall("GetWebPageOptions");
|
|
|
|
// set defaults for IN params
|
|
pOptions->cbSize = sizeof(WEBPAGEOPTIONS);
|
|
pOptions->dwFlags = WPF_HTML|WPF_AUTOINLINE;
|
|
pOptions->dwDelay = 5000;
|
|
pOptions->wchQuote = NULL;
|
|
|
|
if (pfIncludeMsg)
|
|
*pfIncludeMsg=TRUE;
|
|
|
|
// callback to Host for real options
|
|
if (m_pParentCmdTarget)
|
|
{
|
|
if (GetHostFlags(&dwFlags)==S_OK)
|
|
{
|
|
BOOL fSecurityForcesHTMLOff = FALSE;
|
|
VARIANTARG va = {0};
|
|
|
|
va.vt = VT_BOOL;
|
|
va.boolVal = VARIANT_FALSE;
|
|
// read msgs all have the autoinline flag set so we use that along with the option setting to determine
|
|
// if html should be disabled
|
|
if (SUCCEEDED(m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_IS_READ_IN_TEXT_ONLY, OLECMDEXECOPT_DODEFAULT, NULL, &va)))
|
|
fSecurityForcesHTMLOff = (VARIANT_TRUE == va.boolVal) && (dwFlags & MEO_FLAGS_AUTOINLINE);
|
|
|
|
// convert MEHOST_* flags to WPF_*
|
|
pOptions->dwFlags = 0;
|
|
|
|
if (!fSecurityForcesHTMLOff && (dwFlags & MEO_FLAGS_HTML))
|
|
pOptions->dwFlags |= WPF_HTML;
|
|
|
|
if (dwFlags & MEO_FLAGS_AUTOINLINE)
|
|
pOptions->dwFlags |= WPF_AUTOINLINE;
|
|
|
|
if (dwFlags & MEO_FLAGS_SLIDESHOW)
|
|
pOptions->dwFlags |= WPF_SLIDESHOW;
|
|
|
|
if ((!(dwFlags & MEO_FLAGS_INCLUDEMSG)) && pfIncludeMsg)
|
|
*pfIncludeMsg = FALSE;
|
|
}
|
|
|
|
if (m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_QUOTE_CHAR, OLECMDEXECOPT_DODEFAULT, NULL, &va)==S_OK)
|
|
pOptions->wchQuote = (WCHAR)va.lVal;
|
|
|
|
if (m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SLIDESHOW_DELAY, OLECMDEXECOPT_DODEFAULT, NULL, &va)==S_OK)
|
|
pOptions->dwDelay = (WCHAR)va.lVal;
|
|
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: ViewSource
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
HRESULT CBody::ViewSource(BOOL fMessage)
|
|
{
|
|
TraceCall("CBody::ViewSource");
|
|
|
|
if (fMessage)
|
|
MimeEditViewSource(m_hwnd, m_pMsg);
|
|
else
|
|
{
|
|
if (m_pCmdTarget)
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_VIEWSOURCE, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnUIDeactivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnUIDeactivate(BOOL fUndoable)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::OnUIDeactivate");
|
|
|
|
hr = CDocHost::OnUIDeactivate(fUndoable);;
|
|
|
|
if (m_pParentInPlaceSite)
|
|
m_pParentInPlaceSite->OnUIDeactivate(fUndoable);
|
|
|
|
if (m_pParentInPlaceFrame)
|
|
m_pParentInPlaceFrame->SetActiveObject(NULL, NULL);
|
|
|
|
if (m_pFmtBar)
|
|
m_pFmtBar->Update();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: OnUIActivate
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::OnUIActivate()
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::OnUIActivate");
|
|
|
|
hr = CDocHost::OnUIActivate();
|
|
|
|
if (m_pParentInPlaceSite)
|
|
m_pParentInPlaceSite->OnUIActivate();
|
|
|
|
if (m_pParentInPlaceFrame)
|
|
m_pParentInPlaceFrame->SetActiveObject(m_pDocActiveObj, NULL);
|
|
|
|
if (m_pFmtBar)
|
|
m_pFmtBar->Update();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: SetStatusText
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::SetStatusText(LPCOLESTR pszW)
|
|
{
|
|
LPWSTR pszTempW;
|
|
|
|
TraceCall("CDocHost::SetStatusText");
|
|
|
|
// bug #2137. This is a nasty hack. If we get a statusbar update
|
|
// with mid://xxx#bookmark then we tear off the mid://xxx portion
|
|
// to show the name more cleanly.
|
|
// the right fix is for trident to add handling for displaying urls to
|
|
// call IInternetInfo::ParseUrl with PARSE_URL_FRIENDLY
|
|
|
|
if (pszW &&
|
|
StrCmpNIW(pszW, L"mid:", 4) == 0 &&
|
|
(pszTempW = StrPBrkW(pszW, L"#")))
|
|
pszW = pszTempW;
|
|
|
|
if (m_pParentInPlaceFrame)
|
|
m_pParentInPlaceFrame->SetStatusText(pszW);
|
|
|
|
return CDocHost::SetStatusText(pszW);;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: DoRot13
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::DoRot13()
|
|
{
|
|
IHTMLTxtRange *pTxtRange;
|
|
HRESULT hr;
|
|
BSTR bstr;
|
|
LPWSTR lpszW;
|
|
ULONG cb;
|
|
|
|
hr = CreateRange(&pTxtRange);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr=pTxtRange->get_text(&bstr);
|
|
if (!FAILED(hr))
|
|
{
|
|
cb=SysStringLen(bstr);
|
|
|
|
while(cb--)
|
|
{
|
|
register WCHAR chW;
|
|
|
|
chW=bstr[cb];
|
|
if (chW >= 'a' && chW <= 'z')
|
|
bstr[cb] = 'a' + (((chW - 'a') + 13) % 26);
|
|
else if (chW >= 'A' && chW <= 'Z')
|
|
bstr[cb] = 'A' + (((chW - 'A') + 13) % 26);
|
|
|
|
}
|
|
|
|
hr=pTxtRange->put_text(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
pTxtRange->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
void CBody::OutputHeaderText(HDC hdc, LPWSTR psz, int *pcxPos, int cyPos, int cxMax, ULONG uFlags)
|
|
{
|
|
static int yLabel = 0;
|
|
int cch,
|
|
cyOffset = 0;
|
|
SIZE size;
|
|
RECT rc;
|
|
WCHAR szRes[CCHMAX_STRINGRES];
|
|
HFONT hFont;
|
|
|
|
if (IS_INTRESOURCE(psz))
|
|
{
|
|
LoadStringWrapW(g_hLocRes, PtrToUlong(psz), szRes, ARRAYSIZE(szRes));
|
|
psz = szRes;
|
|
}
|
|
|
|
cch = lstrlenW(psz);
|
|
|
|
if (m_pFontCache &&
|
|
m_pFontCache->GetFont((uFlags & HDRTXT_BOLD)?FNT_SYS_ICON_BOLD:FNT_SYS_ICON, (uFlags & HDRTXT_SYSTEMFONT )? NULL : m_hCharset, &hFont)==S_OK)
|
|
SelectObject(hdc, hFont);
|
|
|
|
GetTextExtentPoint32AthW(hdc, psz, cch, &size, DT_NOPREFIX);
|
|
|
|
// bobn: Raid 84705 We need to make sure that the fields line up
|
|
if(yLabel && !(uFlags & (HDRTXT_BOLD|HDRTXT_SYSTEMFONT)))
|
|
{
|
|
cyOffset = yLabel - size.cy;
|
|
}
|
|
|
|
rc.top = cyPos + ((cyOffset < (-2))? (-2) : cyOffset); // bobn: Raid 84705 We need to make sure that the fields line up
|
|
rc.left = *pcxPos;
|
|
rc.right = min(*pcxPos + size.cx + 1, cxMax);
|
|
rc.bottom = cyPos + size.cy + cyOffset; // bobn: Raid 84705 We need to make sure that the fields line up
|
|
DrawTextExWrapW(hdc, psz, cch, &rc, DT_NOPREFIX | DT_WORD_ELLIPSIS, NULL);
|
|
*pcxPos=rc.right;
|
|
|
|
// bobn: Raid 84705 We need to make sure that the fields line up
|
|
if (uFlags & (HDRTXT_BOLD|HDRTXT_SYSTEMFONT))
|
|
{
|
|
yLabel = size.cy;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* control strings:
|
|
* &s - Subject: &c - Cc:
|
|
* &t - To: &d - Date:
|
|
* &f - From:
|
|
*
|
|
*/
|
|
HRESULT CBody::OnPaint()
|
|
{
|
|
HDC hdc,
|
|
hdcMem;
|
|
PAINTSTRUCT ps;
|
|
RECT rc,
|
|
rcBtnBar;
|
|
int idc;
|
|
SIZE sze;
|
|
HBITMAP hbmMem;
|
|
HGDIOBJ hbmOld;
|
|
int cx,
|
|
cxLabels,
|
|
cy,
|
|
cxPos,
|
|
cyPos,
|
|
cyLine;
|
|
int iBackColor,
|
|
iTextColor;
|
|
LPSTR pszFmt;
|
|
LPSTR psz,
|
|
pszLast;
|
|
HFONT hFont;
|
|
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW ||
|
|
m_uHdrStyle == MESTYLE_MINIHEADER)
|
|
{
|
|
hdc=BeginPaint(m_hwnd, &ps);
|
|
|
|
hdcMem = CreateCompatibleDC(hdc);
|
|
idc=SaveDC(hdcMem);
|
|
|
|
// select font so cyLine is accurate
|
|
if (m_pFontCache &&
|
|
m_pFontCache->GetFont(FNT_SYS_ICON_BOLD, NULL, &hFont)==S_OK)
|
|
SelectObject(hdcMem, hFont);
|
|
|
|
GetClientRect(m_hwnd, &rc);
|
|
cx = rc.right;
|
|
cxLabels = cx - (GetSystemMetrics(SM_CXBORDER)*2); // account for client-edge
|
|
cy = lGetClientHeight();
|
|
|
|
if (m_cVisibleBtns)
|
|
{
|
|
// trim cx if buttons are shown
|
|
Assert(IsWindow(m_hwndBtnBar));
|
|
GetClientRect(m_hwndBtnBar, &rcBtnBar);
|
|
cxLabels -= rcBtnBar.right;
|
|
}
|
|
|
|
hbmMem = CreateCompatibleBitmap(hdc, cx, cy);
|
|
hbmOld = SelectObject(hdcMem, (HGDIOBJ)hbmMem);
|
|
|
|
if (m_fFocus)
|
|
{
|
|
iBackColor = COLOR_HEADERFOCUS;
|
|
iTextColor = COLOR_HEADERTXTFOCUS;
|
|
}
|
|
else
|
|
{
|
|
iBackColor = COLOR_HEADER;
|
|
iTextColor = COLOR_HEADERTXT;
|
|
}
|
|
|
|
FillRect(hdcMem, &rc, GetSysColorBrush(iBackColor));
|
|
|
|
cxPos = CX_LABEL_PADDING;
|
|
cyPos = CY_LINE_PADDING;
|
|
|
|
SetBkMode(hdcMem, OPAQUE);
|
|
SetBkColor(hdcMem, GetSysColor(iBackColor));
|
|
SetTextColor(hdcMem, GetSysColor (iTextColor));
|
|
cyLine = lGetLineHeight(hdcMem);
|
|
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW)
|
|
{
|
|
// if we want the full preview header, let's render the text
|
|
pszFmt = m_pszLayout;
|
|
pszLast = pszFmt;
|
|
|
|
while (*pszFmt)
|
|
{
|
|
if (*pszFmt == '&')
|
|
{
|
|
// control character
|
|
pszFmt++;
|
|
switch (*pszFmt)
|
|
{
|
|
case 'f':
|
|
case 'F':
|
|
// from field
|
|
if (*pszFmt=='f' || (m_pszFrom&& *m_pszFrom))
|
|
{
|
|
OutputHeaderText(hdcMem, MAKEINTRESOURCEW(idsFromField), &cxPos, cyPos, cxLabels, HDRTXT_BOLD|HDRTXT_SYSTEMFONT);
|
|
cxPos+=CX_LABEL_PADDING;
|
|
}
|
|
if (m_pszFrom)
|
|
OutputHeaderText(hdcMem, m_pszFrom, &cxPos, cyPos, cxLabels, 0);
|
|
cxPos+=2*CX_LABEL_PADDING;
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
if (*pszFmt=='s' || (m_pszSubject && *m_pszSubject))
|
|
{
|
|
OutputHeaderText(hdcMem, MAKEINTRESOURCEW(idsSubjectField), &cxPos, cyPos, cxLabels, HDRTXT_BOLD|HDRTXT_SYSTEMFONT);
|
|
cxPos+=CX_LABEL_PADDING;
|
|
}
|
|
if (m_pszSubject)
|
|
OutputHeaderText(hdcMem, m_pszSubject, &cxPos, cyPos, cxLabels, 0);
|
|
cxPos+=2*CX_LABEL_PADDING;
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
if (*pszFmt=='c' || (m_pszCc && *m_pszCc))
|
|
{
|
|
OutputHeaderText(hdcMem, MAKEINTRESOURCEW(idsCcField), &cxPos, cyPos, cxLabels, HDRTXT_BOLD|HDRTXT_SYSTEMFONT);
|
|
cxPos+=CX_LABEL_PADDING;
|
|
}
|
|
if (m_pszCc)
|
|
OutputHeaderText(hdcMem, m_pszCc, &cxPos, cyPos, cxLabels, 0);
|
|
cxPos+=2*CX_LABEL_PADDING;
|
|
break;
|
|
|
|
case 't':
|
|
case 'T':
|
|
if (*pszFmt=='t' || (m_pszTo && *m_pszTo))
|
|
{
|
|
OutputHeaderText(hdcMem, MAKEINTRESOURCEW(idsToField), &cxPos, cyPos, cxLabels, HDRTXT_BOLD|HDRTXT_SYSTEMFONT);
|
|
cxPos+=CX_LABEL_PADDING;
|
|
}
|
|
if (m_pszTo)
|
|
OutputHeaderText(hdcMem, m_pszTo, &cxPos, cyPos, cxLabels, 0);
|
|
cxPos+=2*CX_LABEL_PADDING;
|
|
break;
|
|
|
|
case 'b':
|
|
// &b is a line break
|
|
cxPos = CX_LABEL_PADDING;
|
|
cyPos += cyLine + 2; // bobn: Arbitrary pad so that we don't clip to the line below
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
pszFmt++;
|
|
}
|
|
|
|
if (!m_fFocus)
|
|
{
|
|
// if we don't have focus, paint a 3d edge
|
|
rc.top = 0;
|
|
rc.left = 0;
|
|
rc.right = cx;
|
|
rc.bottom = m_cyPreview;
|
|
DrawEdge(hdcMem, &rc, BDR_RAISEDINNER, BF_RECT);
|
|
}
|
|
}
|
|
|
|
BitBlt(hdc, 0, 0, cx, cy, hdcMem, 0, 0, SRCCOPY);
|
|
|
|
if (!m_lpOleObj)
|
|
{
|
|
HBRUSH hBrush = SelectBrush(hdc, GetSysColorBrush(COLOR_WINDOW));
|
|
GetClientRect(m_hwnd, &rc);
|
|
GetDocObjSize(&rc);
|
|
PatBlt(hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, PATCOPY);
|
|
SelectBrush(hdc, hBrush);
|
|
}
|
|
|
|
SelectObject(hdcMem, hbmOld);
|
|
RestoreDC(hdcMem, idc);
|
|
|
|
DeleteObject(hbmMem);
|
|
DeleteDC(hdcMem);
|
|
EndPaint(m_hwnd, &ps);
|
|
return S_OK;
|
|
}
|
|
else
|
|
if (!m_lpOleObj)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
RECT rc;
|
|
HBRUSH hBrush;
|
|
|
|
GetClientRect(m_hwnd, &rc);
|
|
GetDocObjSize(&rc);
|
|
hdc = BeginPaint(m_hwnd, &ps);
|
|
if (m_dwStyle & MEBF_INNERCLIENTEDGE)
|
|
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT|BF_ADJUST);
|
|
hBrush = SelectBrush(hdc, GetSysColorBrush(COLOR_WINDOW));
|
|
PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATCOPY);
|
|
SelectBrush(hdc, hBrush);
|
|
EndPaint(m_hwnd, &ps);
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
LONG CBody::lGetLineHeight(HDC hdc)
|
|
{
|
|
HFONT hfontOld=0,
|
|
hFont;
|
|
TEXTMETRIC tm;
|
|
|
|
if (hdc)
|
|
GetTextMetrics(hdc, &tm);
|
|
else
|
|
{
|
|
// Calculate the height of the line: based on the height of the bold font
|
|
hdc=GetDC(NULL);
|
|
|
|
if (m_pFontCache &&
|
|
m_pFontCache->GetFont(FNT_SYS_ICON, NULL, &hFont)==S_OK)
|
|
hfontOld = (HFONT)SelectObject(hdc, hFont);
|
|
|
|
GetTextMetrics(hdc, &tm);
|
|
|
|
// Set things back
|
|
if (hfontOld)
|
|
SelectObject(hdc, hfontOld);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
|
|
return tm.tmHeight;
|
|
}
|
|
|
|
HRESULT CBody::RecalcPreivewHeight(HDC hdc)
|
|
{
|
|
ULONG cLines=1;
|
|
LPSTR psz;
|
|
RECT rc;
|
|
|
|
// default
|
|
if (!m_pszLayout)
|
|
m_pszLayout = PszDup("&f&t&C&b&s");
|
|
|
|
// cruise thro' the preview layout string and figure out how many line high we are and the
|
|
// physical pixel height of those lines in the current font
|
|
|
|
if (psz = m_pszLayout)
|
|
while (*psz)
|
|
{
|
|
if (*psz == '&' && *(psz+1) == 'b')
|
|
cLines++;
|
|
psz++;
|
|
}
|
|
|
|
m_cyPreview = cLines * lGetLineHeight(hdc);
|
|
m_cyPreview+=2*CY_LINE_PADDING; // padding
|
|
|
|
if (m_hwndBtnBar)
|
|
{
|
|
// if the button bar is show, account for it.
|
|
GetClientRect(m_hwndBtnBar, &rc);
|
|
rc.bottom+= (2*GetSystemMetrics(SM_CYBORDER)); // factor client edge
|
|
m_cyPreview = max (m_cyPreview, (ULONG)rc.bottom);
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::GetDocObjSize(LPRECT prc)
|
|
{
|
|
if(!prc)
|
|
return E_INVALIDARG;
|
|
|
|
if (m_uSrcView == MEST_EDIT)
|
|
prc->top+=lGetClientHeight();
|
|
|
|
if (m_fSrcTabs)
|
|
prc->top+=4;
|
|
|
|
if (m_fSrcTabs)
|
|
{
|
|
RECT rc={0};
|
|
|
|
Assert (m_hwndTab);
|
|
if (TabCtrl_GetItemRect(m_hwndTab, 0, &rc))
|
|
prc->bottom -= (rc.bottom - rc.top) + 2;
|
|
|
|
InflateRect(prc, -4, -4);
|
|
}
|
|
|
|
// make sure we don't make a nonsense rect.
|
|
if(prc->bottom<prc->top)
|
|
prc->bottom=prc->top;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CBody::UpdatePreviewLabels()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
SafeMemFree(m_pszSubject);
|
|
SafeMemFree(m_pszTo);
|
|
SafeMemFree(m_pszCc);
|
|
SafeMemFree(m_pszFrom);
|
|
|
|
if (m_pMsgW)
|
|
{
|
|
m_pMsgW->GetAddressFormatW(IAT_CC, AFT_DISPLAY_FRIENDLY, &m_pszCc);
|
|
|
|
m_pMsgW->GetAddressFormatW(IAT_FROM, AFT_DISPLAY_FRIENDLY, &m_pszFrom);
|
|
|
|
if (FAILED(m_pMsgW->GetAddressFormatW(IAT_TO, AFT_DISPLAY_FRIENDLY, &m_pszTo)))
|
|
MimeOleGetBodyPropW(m_pMsgW, HBODY_ROOT, PIDTOSTR(PID_HDR_NEWSGROUPS), NOFLAGS, &m_pszTo);
|
|
|
|
MimeOleGetBodyPropW(m_pMsgW, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &m_pszSubject);
|
|
}
|
|
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT CBody::OnFocus(BOOL fGotFocus)
|
|
{
|
|
if (m_uHdrStyle == MESTYLE_PREVIEW || m_uHdrStyle == MESTYLE_MINIHEADER)
|
|
{
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
if (m_hwndBtnBar)
|
|
InvalidateRect(m_hwndBtnBar, NULL, TRUE);
|
|
}
|
|
|
|
return CDocHost::OnFocus(fGotFocus);
|
|
}
|
|
|
|
HRESULT CBody::OnEraseBkgnd(HDC hdc)
|
|
{
|
|
RECT rc;
|
|
|
|
GetClientRect(m_hwnd, &rc);
|
|
rc.bottom = lGetClientHeight();
|
|
FillRect(hdc, &rc, GetSysColorBrush(m_fFocus?COLOR_HEADERFOCUS:COLOR_HEADER));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::SetStyle(ULONG uStyle)
|
|
{
|
|
if (uStyle == m_uHdrStyle)
|
|
return S_OK;
|
|
|
|
m_uHdrStyle = uStyle;
|
|
|
|
ShowFormatBar(uStyle == MESTYLE_FORMATBAR);
|
|
ShowPreview(uStyle == MESTYLE_PREVIEW);
|
|
Resize();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
LONG CBody::lGetClientHeight()
|
|
{
|
|
HWND hwndFmtbar;
|
|
RECT rc;
|
|
|
|
// even if we don't have a big header, leave a thin
|
|
// line for users to click
|
|
switch (m_uHdrStyle)
|
|
{
|
|
case MESTYLE_PREVIEW:
|
|
return m_cyPreview;
|
|
|
|
case MESTYLE_MINIHEADER:
|
|
return SMALLHEADERHEIGHT;
|
|
|
|
case MESTYLE_FORMATBAR:
|
|
|
|
hwndFmtbar = GetDlgItem(m_hwnd, idcFmtBar);
|
|
|
|
if (hwndFmtbar)
|
|
{
|
|
GetClientRect(hwndFmtbar, &rc);
|
|
return rc.bottom-rc.top;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT CBody::Resize()
|
|
{
|
|
RECT rc;
|
|
|
|
GetClientRect(m_hwnd, &rc);
|
|
WMSize(rc.right-rc.left, rc.bottom-rc.top);
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::SetCharset(HCHARSET hCharset)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
BSTR bstr;
|
|
TCHAR rgchCset[CCHMAX_CSET_NAME];
|
|
|
|
if (hCharset == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
if (m_hCharset == hCharset)
|
|
return S_OK;
|
|
|
|
if (!m_pDoc)
|
|
return E_UNEXPECTED;
|
|
|
|
if (m_fDesignMode)
|
|
{
|
|
// if we're in edit-mode, then we can't reload the document, so we use the trident object
|
|
// model to tell it the charset we want...
|
|
hr = HrGetMetaTagName(hCharset, rgchCset, ARRAYSIZE(rgchCset));
|
|
if (!FAILED(hr))
|
|
{
|
|
hr = HrLPSZToBSTR(rgchCset, &bstr);
|
|
if (!FAILED(hr))
|
|
{
|
|
// this will cause trident to reload with a new meta tag, if the user has changed the charset
|
|
// on the view|language menu
|
|
hr=m_pDoc->put_charset(bstr);
|
|
if (!FAILED(hr))
|
|
{
|
|
// all went well - switch the language
|
|
m_hCharset = hCharset;
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if lookup in mime database fails, return a good error
|
|
hr = MIMEEDIT_E_CHARSETNOTFOUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if we're in browse mode, let the base class take care of reloading the document
|
|
if (m_pMsg)
|
|
{
|
|
hr = m_pMsg->SetCharset(hCharset, CSET_APPLY_ALL);
|
|
if (!FAILED(hr))
|
|
{
|
|
IMimeMessage *pMsg=m_pMsg;
|
|
pMsg->AddRef();
|
|
hr = Load(pMsg);
|
|
pMsg->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::PrivateEnableModeless(BOOL fEnable)
|
|
{
|
|
if (m_pInPlaceActiveObj)
|
|
m_pInPlaceActiveObj->EnableModeless(fEnable);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT CBody::ClearDirtyFlag()
|
|
{
|
|
return HrSetDirtyFlagImpl(m_pDoc, FALSE);
|
|
}
|
|
|
|
|
|
|
|
DWORD CBody::DwChooseProperties()
|
|
{
|
|
DWORD dwRet=0;
|
|
|
|
// if we are on an image
|
|
if(m_fOnImage)
|
|
{
|
|
m_fOnImage = 0;
|
|
return IDM_IMAGE;
|
|
}
|
|
|
|
// if we are on an anchor
|
|
if (GetSelectedAnchor(NULL)==S_OK)
|
|
return IDM_HYPERLINK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CBody::ShowFormatBar(BOOL fOn)
|
|
{
|
|
Assert (m_pFmtBar); // created in Init
|
|
|
|
if (fOn)
|
|
return m_pFmtBar->Show();
|
|
else
|
|
return m_pFmtBar->Hide();
|
|
}
|
|
|
|
|
|
void CBody::WMSize(int cxBody, int cyBody)
|
|
{
|
|
RECT rc,
|
|
rcBar;
|
|
|
|
switch (m_uHdrStyle)
|
|
{
|
|
case MESTYLE_FORMATBAR:
|
|
if (m_hwnd)
|
|
{
|
|
HWND hwndFmtbar = GetDlgItem(m_hwnd, idcFmtBar);
|
|
|
|
if (hwndFmtbar)
|
|
{
|
|
// if the format bar is on adjust the window rect accordingly
|
|
GetClientRect(hwndFmtbar, &rc);
|
|
SetWindowPos(hwndFmtbar, NULL, m_fSrcTabs?4:0, m_fSrcTabs?4:0, cxBody-(m_fSrcTabs?8:0), rc.bottom-rc.top, SWP_NOACTIVATE|SWP_NOZORDER);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESTYLE_PREVIEW:
|
|
// center toolbar in the preview header and right-align it (note: account for the client edge we paint)
|
|
if (m_hwndBtnBar)
|
|
{
|
|
GetClientRect(m_hwndBtnBar, &rcBar);
|
|
SetWindowPos(m_hwndBtnBar, NULL, cxBody - rcBar.right - (GetSystemMetrics(SM_CXBORDER)*2), (m_cyPreview - rcBar.bottom)/2, 0, 0,SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_NOSIZE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (m_fSrcTabs)
|
|
{
|
|
RECT rc;
|
|
|
|
Assert (m_hwndTab);
|
|
GetClientRect(m_hwnd, &rc);
|
|
GetDocObjSize(&rc);
|
|
|
|
SetWindowPos(m_hwndTab, HWND_BOTTOM, 0, 0, cxBody, cyBody, SWP_NOACTIVATE);
|
|
if (m_pSrcView)
|
|
m_pSrcView->SetRect(&rc);
|
|
}
|
|
CDocHost::WMSize(cxBody, cyBody);
|
|
}
|
|
|
|
HRESULT CBody::InsertTextAtCaret(BSTR bstr, BOOL fHtml, BOOL fMoveCaretToEnd)
|
|
{
|
|
IHTMLTxtRange *pTxtRange=0;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
if (!FAILED(hr = GetSelection(&pTxtRange)))
|
|
{
|
|
if(fHtml)
|
|
hr=pTxtRange->pasteHTML(bstr);
|
|
else
|
|
hr=pTxtRange->put_text(bstr);
|
|
|
|
pTxtRange->collapse( fMoveCaretToEnd ? VARIANT_FALSE : VARIANT_TRUE);
|
|
pTxtRange->select();
|
|
pTxtRange->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::UpdateCommands()
|
|
{
|
|
IHTMLTxtRange *pTxtRange=0;
|
|
|
|
if (m_pFmtBar)
|
|
m_pFmtBar->Update();
|
|
|
|
if (m_pParentCmdTarget)
|
|
m_pParentCmdTarget->Exec(NULL, OLECMDID_UPDATECOMMANDS, 0, NULL, NULL);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
LRESULT CBody::WMNotify(WPARAM wParam, NMHDR* pnmhdr)
|
|
{
|
|
HFONT hFont;
|
|
LRESULT lRet;
|
|
|
|
if (m_pSrcView &&
|
|
m_pSrcView->OnWMNotify(wParam, pnmhdr, &lRet)==S_OK)
|
|
return lRet;
|
|
|
|
switch (wParam)
|
|
{
|
|
case idcTabs:
|
|
switch (pnmhdr->code)
|
|
{
|
|
case TCN_SELCHANGE:
|
|
ShowSourceView(TabCtrl_GetCurSel(m_hwndTab));
|
|
break;
|
|
|
|
case TCN_SELCHANGING:
|
|
if (m_fReloadingSrc)
|
|
{
|
|
// while loading, prevent switching of tabs
|
|
MessageBeep(MB_ICONSTOP);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case idcFmtBar:
|
|
switch (pnmhdr->code)
|
|
{
|
|
case FBN_GETMENUFONT:
|
|
hFont = 0;
|
|
if (m_pFontCache)
|
|
m_pFontCache->GetFont(FNT_SYS_MENU, NULL, &hFont);
|
|
|
|
return (LPARAM)hFont;
|
|
|
|
case FBN_BODYSETFOCUS:
|
|
UIActivate(FALSE);
|
|
UIActivate(TRUE);
|
|
return 0;
|
|
|
|
case FBN_BODYHASFOCUS:
|
|
return m_fFocus;
|
|
}
|
|
break;
|
|
|
|
case idcBtnBar:
|
|
switch (pnmhdr->code)
|
|
{
|
|
case NM_RCLICK:
|
|
if (((NMCLICK *)pnmhdr)->dwItemSpec == idmPanePaperclip)
|
|
ShowAttachMenu(TRUE);
|
|
break;
|
|
|
|
case TBN_DROPDOWN:
|
|
if (((TBNOTIFY *)pnmhdr)->iItem == idmPanePaperclip)
|
|
ShowAttachMenu(FALSE);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::SetHostComposeFont()
|
|
{
|
|
VARIANTARG va;
|
|
|
|
va.bstrVal = NULL;
|
|
|
|
if (m_pParentCmdTarget)
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_COMPOSE_FONT, OLECMDEXECOPT_DODEFAULT, NULL, &va);
|
|
|
|
SetComposeFont(va.bstrVal);
|
|
SysFreeString(va.bstrVal);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::SetComposeFont(BSTR bstr)
|
|
{
|
|
VARIANTARG va;
|
|
|
|
va.vt = VT_BOOL;
|
|
va.boolVal = bstr?VARIANT_TRUE:VARIANT_FALSE;
|
|
|
|
if (m_pCmdTarget)
|
|
{
|
|
// turn OFF HTML-Edit mode if bstr==NULL
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_HTMLEDITMODE, OLECMDEXECOPT_DODEFAULT, &va, NULL);
|
|
if (bstr)
|
|
{
|
|
va.vt = VT_BSTR;
|
|
va.bstrVal = bstr;
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_COMPOSESETTINGS, OLECMDEXECOPT_DODEFAULT, &va, NULL);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::ClearUndoStack()
|
|
{
|
|
IOleUndoManager *pUndoManager;
|
|
IServiceProvider *pSP;
|
|
HRESULT hr;
|
|
|
|
if (!m_lpOleObj)
|
|
return E_FAIL;
|
|
|
|
if (!FAILED(hr=m_lpOleObj->QueryInterface(IID_IServiceProvider, (LPVOID *)&pSP)))
|
|
{
|
|
if (!FAILED(hr = pSP->QueryService(SID_SOleUndoManager, IID_IOleUndoManager, (LPVOID *)&pUndoManager)))
|
|
{
|
|
hr = pUndoManager->DiscardFrom(NULL);
|
|
pUndoManager->Release();
|
|
}
|
|
pSP->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::DowngradeToPlainText(BOOL fForceFixedFont)
|
|
{
|
|
IHTMLElement *pElem=0;
|
|
IHTMLStyle *pStyle=0;
|
|
IHTMLTxtRange *pTxtRange;
|
|
|
|
BSTR bstr=0;
|
|
|
|
m_pDoc->get_body(&pElem);
|
|
if (pElem)
|
|
{
|
|
pElem->get_innerText(&bstr); // might be NULL if only images
|
|
pElem->put_innerText(bstr);
|
|
HrRemoveStyleSheets(m_pDoc);
|
|
HrRemoveBackground(m_pDoc);
|
|
SetWindowBgColor(TRUE); // force back to default color
|
|
SysFreeString(bstr);
|
|
|
|
if (fForceFixedFont)
|
|
{
|
|
// add <STYLE: font-family: monospace> to the body tag
|
|
pElem->get_style(&pStyle);
|
|
if (pStyle)
|
|
{
|
|
pStyle->put_fontFamily((BSTR)c_bstr_MonoSpace);
|
|
pStyle->Release();
|
|
}
|
|
}
|
|
|
|
if (m_fDesignMode)
|
|
{
|
|
// set caret at start
|
|
if (!FAILED(CreateRange(&pTxtRange)))
|
|
{
|
|
pTxtRange->collapse(VARIANT_TRUE);
|
|
pTxtRange->select();
|
|
pTxtRange->Release();
|
|
}
|
|
}
|
|
|
|
pElem->Release();
|
|
}
|
|
|
|
ClearUndoStack();
|
|
return S_OK;
|
|
}
|
|
|
|
// Returns MIME_S_CHARSET_CONFLICT if can't convert
|
|
HRESULT CBody::SafeToEncodeText(ULONG ulCodePage)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHTMLElement *pElem = NULL;
|
|
BSTR bstr = NULL;
|
|
DWORD dwTemp = 0;
|
|
INT cbIn;
|
|
|
|
m_pDoc->get_body(&pElem);
|
|
if (pElem)
|
|
{
|
|
pElem->get_innerText(&bstr);
|
|
if (NULL != bstr)
|
|
{
|
|
cbIn = SysStringByteLen(bstr);
|
|
|
|
IF_FAILEXIT(hr = ConvertINetString(&dwTemp, CP_UNICODE, ulCodePage, (LPCSTR)bstr, &cbIn, NULL, NULL));
|
|
if (S_FALSE == hr)
|
|
hr = MIME_S_CHARSET_CONFLICT;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
ReleaseObj(pElem);
|
|
SysFreeString(bstr);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::SetDocumentText(BSTR bstr)
|
|
{
|
|
IHTMLTxtRange *pTxtRange;
|
|
HRESULT hr;
|
|
|
|
if (!m_lpOleObj)
|
|
return CO_E_NOT_SUPPORTED;
|
|
|
|
hr = CreateRange(&pTxtRange);
|
|
if (!FAILED(hr))
|
|
{
|
|
hr=pTxtRange->pasteHTML(bstr);
|
|
if (!FAILED(hr))
|
|
{
|
|
ClearDirtyFlag();
|
|
// we not empty anymore
|
|
m_fEmpty=FALSE;
|
|
}
|
|
pTxtRange->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::FormatFont()
|
|
{
|
|
return m_pCmdTarget?m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_FONT, OLECMDEXECOPT_DODEFAULT, NULL, NULL):E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::PasteReplyHeader()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
ULONG uHdrStyle=0;
|
|
LPSTREAM pstm;
|
|
DWORD dwFlags=0,
|
|
dwHdr;
|
|
IHTMLTxtRange *pRange;
|
|
IOleCommandTarget *pCmdTarget;
|
|
VARIANTARG va;
|
|
|
|
if (m_pParentCmdTarget)
|
|
{
|
|
if (m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_HEADER_TYPE, 0, NULL, &va)==S_OK)
|
|
uHdrStyle = va.lVal;
|
|
GetHostFlags(&dwFlags);
|
|
}
|
|
|
|
if ((uHdrStyle != MEHEADER_NONE) &&
|
|
(dwFlags & MEO_FLAGS_INCLUDEMSG))
|
|
{
|
|
// check to see if the rootstream inserted the auto reply header placeholder
|
|
// if so, replace it
|
|
Assert ((uHdrStyle & MEHEADER_NEWS) || (uHdrStyle & MEHEADER_MAIL));
|
|
|
|
dwHdr = HDR_PADDING|(dwFlags&MEO_FLAGS_HTML?HDR_HTML:HDR_PLAIN);
|
|
|
|
if (uHdrStyle & MEHEADER_NEWS)
|
|
dwHdr |= HDR_NEWSSTYLE;
|
|
|
|
if (uHdrStyle & MEHEADER_FORCE_ENGLISH)
|
|
dwHdr |= HDR_HARDCODED;
|
|
|
|
pstm = NULL;
|
|
if (SUCCEEDED(hr=GetHeaderTable(m_pMsgW, NULL, dwHdr, &pstm)))
|
|
{
|
|
BSTR bstr;
|
|
|
|
if (SUCCEEDED(hr = HrIStreamWToBSTR(pstm, &bstr)))
|
|
{
|
|
// paste reply-headers at the top of the message
|
|
InsertBodyText(bstr, IBTF_URLHIGHLIGHT);
|
|
SysFreeString(bstr);
|
|
}
|
|
pstm->Release();
|
|
}
|
|
|
|
if (dwFlags & MEO_FLAGS_BLOCKQUOTE)
|
|
{
|
|
// block quote the text
|
|
if (SUCCEEDED(CreateRange(&pRange)))
|
|
{
|
|
if (SUCCEEDED(pRange->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&pCmdTarget)))
|
|
{
|
|
if (pCmdTarget->Exec(&CMDSETID_Forms3, IDM_INDENT, NULL, NULL, NULL)==S_OK)
|
|
{
|
|
if (m_pParentCmdTarget &&
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_REPLY_TICK_COLOR, 0, 0, &va)==S_OK)
|
|
FormatBlockQuote((COLORREF)va.lVal);
|
|
}
|
|
pCmdTarget->Release();
|
|
}
|
|
pRange->Release();
|
|
}
|
|
}
|
|
|
|
|
|
if (dwFlags & MEO_FLAGS_DONTSPELLCHECKQUOTED)
|
|
CreateRange(&m_pRangeIgnoreSpell);
|
|
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::InsertBodyText(BSTR bstrPaste, DWORD dwFlags)
|
|
{
|
|
IHTMLElement *pElem;
|
|
IHTMLBodyElement *pBodyElem;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
if (!FAILED(GetBodyElement(&pBodyElem)))
|
|
{
|
|
if (!FAILED(pBodyElem->QueryInterface(IID_IHTMLElement, (LPVOID *)&pElem)))
|
|
{
|
|
hr = pElem->insertAdjacentHTML((BSTR)(dwFlags & IBTF_INSERTATEND ?c_bstr_BeforeEnd:c_bstr_AfterBegin), bstrPaste);
|
|
|
|
IHTMLTxtRange *pRange;
|
|
LONG cch;
|
|
|
|
if (!FAILED(hr) && (dwFlags & IBTF_URLHIGHLIGHT))
|
|
{
|
|
if (CreateRangeFromElement(pElem, &pRange)==S_OK)
|
|
{
|
|
if (!FAILED(pRange->collapse(VARIANT_TRUE)))
|
|
{
|
|
if (!FAILED(pRange->moveEnd((BSTR)c_bstr_Character, (LONG)SysStringLen(bstrPaste), (LPLONG)&cch)) && cch!=0)
|
|
UrlHighlight(pRange);
|
|
}
|
|
pRange->Release();
|
|
}
|
|
}
|
|
|
|
pElem->Release();
|
|
}
|
|
pBodyElem->Release();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::FormatBlockQuote(COLORREF crTextColor)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLElementCollection *pCollect;
|
|
IHTMLStyle *pStyle=0;
|
|
IHTMLElement *pElem;
|
|
ULONG cItems;
|
|
TCHAR rgch[CCHMAX_STRINGRES];
|
|
BSTR bstr;
|
|
VARIANT v;
|
|
|
|
if (!FAILED(HrGetCollectionOf(m_pDoc, (BSTR)c_bstr_BLOCKQUOTE, &pCollect)))
|
|
{
|
|
cItems = (int)UlGetCollectionCount(pCollect);
|
|
if (cItems > 0 &&
|
|
!FAILED(HrGetCollectionItem(pCollect, 0, IID_IHTMLElement, (LPVOID *)&pElem)))
|
|
{
|
|
pElem->get_style(&pStyle);
|
|
if (pStyle)
|
|
{
|
|
// set the style on the elemen
|
|
// .replyTick { border-left:solid ; border-left-width: 4;
|
|
// border-color: #0000ff; padding-left: 5;
|
|
// margin-left: 5}
|
|
// 5/19/98: fix margin on nesting:
|
|
// padding-right: 0, margin-right:0
|
|
wnsprintf(rgch, ARRAYSIZE(rgch), "solid 2 #%02x%02x%02x", GetRValue(crTextColor), GetGValue(crTextColor), GetBValue(crTextColor));
|
|
|
|
if (HrLPSZToBSTR(rgch, &bstr)==S_OK)
|
|
{
|
|
pStyle->put_borderLeft(bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
|
|
// set the padding and the margin
|
|
v.vt = VT_I4;
|
|
v.lVal = 5;
|
|
pStyle->put_paddingLeft(v);
|
|
pStyle->put_marginLeft(v);
|
|
v.lVal = 0;
|
|
pStyle->put_paddingRight(v);
|
|
pStyle->put_marginRight(v);
|
|
pStyle->Release();
|
|
}
|
|
// first dude in the collection should be the one at the root of the tree
|
|
pElem->Release();
|
|
}
|
|
pCollect->Release();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::GetAutoText(BSTR *pbstr, BOOL *pfTop)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fSig;
|
|
ULONG uSigOpt;
|
|
TCHAR rgchAutoText[4096]; // buffer big enough to build autotext into
|
|
VARIANTARG va;
|
|
|
|
if (!m_pParentCmdTarget)
|
|
return E_FAIL;
|
|
|
|
if (pfTop)
|
|
*pfTop = TRUE;
|
|
|
|
*rgchAutoText = 0;
|
|
|
|
va.vt = VT_I4;
|
|
va.lVal = MESIG_AUTO;
|
|
fSig = (m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SIGNATURE_ENABLED, 0, &va, NULL)==S_OK);
|
|
if (fSig &&
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SIGNATURE_OPTIONS, 0, NULL, &va)==S_OK)
|
|
{
|
|
Assert(va.vt==VT_I4);
|
|
uSigOpt = va.lVal;
|
|
}
|
|
|
|
/*
|
|
let's build a BSTR of the HTML we want to insert.
|
|
|
|
<DIV>
|
|
<SPAN id=\"__CaretPos__\"><BR></SPAN>
|
|
</DIV>
|
|
<DIV>
|
|
<SPAN id="__Signature__"></SPAN>
|
|
</DIV>
|
|
*/
|
|
|
|
StrCatBuff(rgchAutoText, c_szHtml_DivOpen, ARRAYSIZE(rgchAutoText));
|
|
StrCatBuff(rgchAutoText, c_szCaretSpanTag, ARRAYSIZE(rgchAutoText));
|
|
StrCatBuff(rgchAutoText, c_szHtml_DivClose, ARRAYSIZE(rgchAutoText));
|
|
|
|
if (fSig)
|
|
{
|
|
StrCatBuff(rgchAutoText, c_szHtml_DivOpen, ARRAYSIZE(rgchAutoText));
|
|
StrCatBuff(rgchAutoText, c_szSignatureSpanTag, ARRAYSIZE(rgchAutoText));
|
|
StrCatBuff(rgchAutoText, c_szHtml_DivClose, ARRAYSIZE(rgchAutoText));
|
|
}
|
|
|
|
if (pfTop && fSig && uSigOpt & MESIGOPT_BOTTOM)
|
|
*pfTop = FALSE;
|
|
|
|
// can use ACP for this conversion as we know all the text is lowansi
|
|
return HrLPSZToBSTR(rgchAutoText, pbstr);
|
|
}
|
|
|
|
HRESULT CBody::PasteAutoText()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
LPSTREAM pstm;
|
|
DWORD dwFlags=0;
|
|
BSTR bstrAutoText=0,
|
|
bstrSig=0;
|
|
IHTMLElement *pElem;
|
|
BOOL fPasteAtTop=TRUE;
|
|
DWORD dwSigOpt=0;
|
|
VARIANTARG va;
|
|
|
|
/*
|
|
For AutoText, we search for a <SPAN> tag that indicates where the caret should be.
|
|
To find the caret, we use the following rules
|
|
1. look for "_AthCaret" this is used by a stationery author to set the caret for the stationery
|
|
2. look for "__Ath_AutoCaret" this is inserted by the rootstream it maybe at the top or bottom (sigopts)
|
|
of the document. It should always be present if autotext is needed
|
|
|
|
when we find the caret, we insert the auto-text stream we build (contains compose font and/or signature) and
|
|
put the caret at the start of this.
|
|
*/
|
|
|
|
GetHostFlags(&dwFlags);
|
|
|
|
if (dwFlags & MEO_FLAGS_AUTOTEXT &&
|
|
!FAILED(GetAutoText(&bstrAutoText, &fPasteAtTop)))
|
|
{
|
|
// try a stationery-authored caret
|
|
if (FAILED(ReplaceElement("_AthCaret", bstrAutoText, TRUE)))
|
|
{
|
|
// if there wasn't one, then put at start or end of the body
|
|
// depending on signature
|
|
InsertBodyText(bstrAutoText, fPasteAtTop ? 0:IBTF_INSERTATEND);
|
|
}
|
|
|
|
va.vt = VT_I4;
|
|
va.lVal = MESIG_AUTO;
|
|
|
|
// let's try and insert the signature now.
|
|
if (m_pParentCmdTarget &&
|
|
(m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SIGNATURE_ENABLED, 0, &va, NULL)==S_OK) &&
|
|
(m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SIGNATURE_OPTIONS, 0, NULL, &va)==S_OK) &&
|
|
va.vt==VT_I4)
|
|
{
|
|
dwSigOpt = va.lVal;
|
|
if (m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SIGNATURE, 0, NULL, &va)==S_OK &&
|
|
va.vt==VT_BSTR)
|
|
{
|
|
bstrSig = va.bstrVal;
|
|
// sleazy v2. workaround. Trident pulled out the springloader font code from
|
|
// CElem::put_outerText, so plain-signatures etc no longer go in in the
|
|
// compose font. We fix this by inserting an   after the signature span
|
|
// (if there is one) then we can select the nbsp; and paste at that range.
|
|
if (!FAILED(GetElement(c_szSignatureSpan, &pElem)))
|
|
{
|
|
IHTMLTxtRange *pRange;
|
|
|
|
if (CreateRangeFromElement(pElem, &pRange)==S_OK)
|
|
{
|
|
// small change. Now we put in <SPAN> we move the range to
|
|
// the span and then expand it a char left and right. This range includes the span
|
|
// do it get's nuked when we paste.
|
|
LONG cch;
|
|
|
|
SideAssert(pRange->moveEnd((BSTR)c_bstr_Character, 1, &cch)==S_OK && cch==1);
|
|
SideAssert(pRange->moveStart((BSTR)c_bstr_Character, -1, &cch)==S_OK && cch==-1);
|
|
|
|
if (dwSigOpt & MESIGOPT_HTML)
|
|
pRange->pasteHTML(bstrSig);
|
|
else
|
|
{
|
|
if(dwSigOpt & MESIGOPT_PREFIX)
|
|
{
|
|
BSTR bstrPrefix;
|
|
|
|
// if a prefix is required (for news), then append one
|
|
if (HrLPSZToBSTR(c_szSigPrefix, &bstrPrefix)==S_OK)
|
|
{
|
|
pRange->put_text(bstrPrefix);
|
|
pRange->collapse(VARIANT_FALSE);
|
|
SysFreeString(bstrPrefix);
|
|
}
|
|
}
|
|
pRange->put_text(bstrSig);
|
|
}
|
|
pRange->Release();
|
|
}
|
|
pElem->Release();
|
|
}
|
|
SysFreeString(bstrSig);
|
|
}
|
|
}
|
|
// if we sucessfully pasted, set the caret at the span marker we
|
|
// just pasted
|
|
if (!FAILED(GetElement(c_szCaretSpan, &pElem)))
|
|
{
|
|
SelectElement(pElem, TRUE);
|
|
DeleteElement(pElem);
|
|
pElem->Release();
|
|
}
|
|
|
|
SysFreeString(bstrAutoText);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::GetHostFlags(LPDWORD pdwFlags)
|
|
{
|
|
VARIANTARG va;
|
|
HRESULT hr=E_FAIL;
|
|
|
|
*pdwFlags = 0;
|
|
|
|
if (m_pParentCmdTarget &&
|
|
!FAILED(hr = m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_FLAGS, 0, NULL, &va)))
|
|
*pdwFlags = va.lVal;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::GetBackgroundColor(DWORD *pdwColor)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLBodyElement *pBodyElem=0;
|
|
VARIANT var;
|
|
TCHAR *lpszColor = NULL;
|
|
|
|
var.vt = VT_BSTR;
|
|
var.bstrVal = NULL;
|
|
|
|
hr = GetBodyElement(&pBodyElem);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = pBodyElem->get_bgColor(&var);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if (var.bstrVal)
|
|
{
|
|
hr = HrBSTRToLPSZ(CP_ACP, var.bstrVal, &lpszColor);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
// get_bgColor returns format "#RRGGBB"
|
|
// Only send in "RRGGBB" part of the string
|
|
GetRGBFromString(pdwColor, lpszColor+1);
|
|
}
|
|
else
|
|
*pdwColor = 0x00FFFFFF | GetSysColor(COLOR_WINDOW);
|
|
|
|
error:
|
|
ReleaseObj(pBodyElem);
|
|
SafeMemFree(lpszColor);
|
|
SysFreeString(var.bstrVal);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::SetBackgroundColor(DWORD dwColor)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLBodyElement *pBodyElem=0;
|
|
VARIANT var;
|
|
TCHAR szColor[7]; //"#RRGGBB\0"
|
|
BSTR bstrColor = NULL;
|
|
|
|
GetStringRGB(dwColor, szColor);
|
|
hr = HrLPSZToBSTR(szColor, &bstrColor);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
var.vt = VT_BSTR;
|
|
var.bstrVal = bstrColor;
|
|
|
|
hr = GetBodyElement(&pBodyElem);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = pBodyElem->put_bgColor(var);
|
|
|
|
error:
|
|
ReleaseObj(pBodyElem);
|
|
SysFreeString(bstrColor);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::SetWindowBgColor(BOOL fForce)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLBodyElement *pBodyElem=0;
|
|
CHAR szBuf[MAX_PATH] = {0};
|
|
DWORD dColors = 0;
|
|
VARIANT v1, v2;
|
|
|
|
v1.vt = VT_BSTR;
|
|
v1.bstrVal = NULL;
|
|
v2.vt = VT_BSTR;
|
|
v2.bstrVal = NULL;
|
|
|
|
hr = GetBodyElement(&pBodyElem);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr=pBodyElem->get_bgColor(&v1);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
if(NULL != v1.bstrVal && !fForce)
|
|
goto error;
|
|
|
|
dColors = GetSysColor(COLOR_WINDOW);
|
|
GetStringRGB(dColors, szBuf);
|
|
|
|
hr=HrLPSZToBSTR(szBuf, &(v2.bstrVal));
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr=pBodyElem->put_bgColor(v2);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
error:
|
|
ReleaseObj(pBodyElem);
|
|
SysFreeString(v1.bstrVal);
|
|
SysFreeString(v2.bstrVal);
|
|
return hr;
|
|
}
|
|
|
|
#define CCBMAX_FRAMESEARCH 4096
|
|
|
|
HRESULT CBody::InsertFile(BSTR bstrFileName)
|
|
{
|
|
OPENFILENAMEW ofn;
|
|
WCHAR wszFile[MAX_PATH],
|
|
wszTitle[CCHMAX_STRINGRES],
|
|
wszFilter[100],
|
|
wszDefExt[30];
|
|
BYTE pbHtml[CCBMAX_FRAMESEARCH];
|
|
HRESULT hr;
|
|
LPSTREAM pstm = NULL;
|
|
DWORD dwFlags=0;
|
|
BOOL fHtml;
|
|
ULONG cb=0;
|
|
|
|
if (!m_lpOleObj)
|
|
IF_FAILEXIT(hr = E_FAIL);
|
|
|
|
*wszFile = 0;
|
|
*wszTitle = 0;
|
|
*wszFilter = 0;
|
|
*wszDefExt = 0;
|
|
|
|
// Load Res Strings
|
|
GetHostFlags(&dwFlags);
|
|
|
|
if (bstrFileName)
|
|
{
|
|
// if we have a filename, let's use that else prompt for one
|
|
StrCpyNW(wszFile, (LPWSTR)bstrFileName, ARRAYSIZE(wszFile));
|
|
}
|
|
else
|
|
{
|
|
LoadStringWrapW(g_hLocRes, dwFlags&MEO_FLAGS_HTML?idsTextOrHtmlFileFilter:idsTextFileFilter, wszFilter, ARRAYSIZE(wszFilter));
|
|
ReplaceCharsW(wszFilter, L'|', L'\0');
|
|
LoadStringWrapW(g_hLocRes, idsDefTextExt, wszDefExt, ARRAYSIZE(wszDefExt));
|
|
LoadStringWrapW(g_hLocRes, idsInsertTextTitle, wszTitle, ARRAYSIZE(wszTitle));
|
|
|
|
// Setup Save file struct
|
|
ZeroMemory (&ofn, sizeof (ofn));
|
|
ofn.lStructSize = sizeof (ofn);
|
|
ofn.hwndOwner = m_hwnd;
|
|
ofn.lpstrFilter = wszFilter;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = wszFile;
|
|
ofn.nMaxFile = ARRAYSIZE(wszFile);
|
|
ofn.lpstrTitle = wszTitle;
|
|
ofn.lpstrDefExt = wszDefExt;
|
|
ofn.Flags = OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_NOCHANGEDIR;
|
|
|
|
// Show OpenFile Dialog
|
|
if (!GetOpenFileNameWrapW(&ofn))
|
|
return NOERROR;
|
|
}
|
|
|
|
if (*wszFile==NULL)
|
|
IF_FAILEXIT(hr = E_FAIL);
|
|
|
|
IF_FAILEXIT(hr=OpenFileStreamW(wszFile, OPEN_EXISTING, GENERIC_READ, &pstm));
|
|
|
|
fHtml = (dwFlags&MEO_FLAGS_HTML)&&FIsHTMLFileW(wszFile);
|
|
|
|
if (fHtml)
|
|
{
|
|
BOOL fFrames = FALSE;
|
|
BOOL fLittleEndian;
|
|
|
|
// if html, do a quick scan of the first 2k for a frameset
|
|
pstm->Read(pbHtml, sizeof(pbHtml) - 2, &cb);
|
|
pbHtml[cb] = 0;
|
|
pbHtml[cb+1] = 0;
|
|
|
|
// if we found a frameset tag, warn the user
|
|
if (S_OK == HrIsStreamUnicode(pstm, &fLittleEndian))
|
|
{
|
|
if(StrStrIW((WCHAR*)pbHtml, L"<FRAMESET"))
|
|
fFrames = TRUE;
|
|
}
|
|
else if (StrStrI((CHAR*)pbHtml, "<FRAMESET"))
|
|
fFrames = TRUE;
|
|
|
|
if (fFrames)
|
|
AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsInsertTextTitle), MAKEINTRESOURCEW(idsErrInsertFileHasFrames), NULL, MB_OK);
|
|
}
|
|
|
|
IF_FAILEXIT(hr=InsertStreamAtCaret(pstm, fHtml));
|
|
|
|
exit:
|
|
ReleaseObj(pstm);
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::InsertStreamAtCaret(LPSTREAM pstm, BOOL fHtml)
|
|
{
|
|
BSTR bstr;
|
|
HRESULT hr;
|
|
UINT uiCodePage = 0 ;
|
|
INETCSETINFO CsetInfo ;
|
|
LPSTR pszCharset=NULL;
|
|
HCHARSET hCharset=NULL;
|
|
IStream *pstm2;
|
|
|
|
HrRewindStream(pstm);
|
|
|
|
if (fHtml)
|
|
{
|
|
if (SUCCEEDED(MimeOleCreateVirtualStream(&pstm2)))
|
|
{
|
|
if (SUCCEEDED(HrCopyStream(pstm, pstm2, 0)))
|
|
{
|
|
// if HTML then try and SNIFF the charset from the document
|
|
if (GetHtmlCharset(pstm2, &pszCharset)==S_OK)
|
|
{
|
|
MimeOleFindCharset(pszCharset, &hCharset);
|
|
MemFree(pszCharset);
|
|
}
|
|
}
|
|
|
|
// Free up the stream
|
|
pstm2->Release();
|
|
}
|
|
}
|
|
|
|
// if nothing so far, try the message-charset
|
|
if (!hCharset)
|
|
hCharset = m_hCharset;
|
|
|
|
if (hCharset)
|
|
{
|
|
// get CodePage from HCHARSET
|
|
MimeOleGetCharsetInfo(hCharset,&CsetInfo);
|
|
uiCodePage = CsetInfo.cpiInternet;
|
|
}
|
|
|
|
hr=HrIStreamToBSTR(uiCodePage ? uiCodePage : GetACP(), pstm, &bstr);
|
|
if (!(FAILED(hr)))
|
|
{
|
|
hr = InsertTextAtCaret(bstr, fHtml, TRUE);
|
|
SysFreeString(bstr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CALLBACK FreeDataObj(PDATAOBJINFO pDataObjInfo, DWORD celt)
|
|
{
|
|
// Loop through the data and free it all
|
|
if (pDataObjInfo)
|
|
{
|
|
for (DWORD i = 0; i < celt; i++)
|
|
SafeMemFree(pDataObjInfo[i].pData);
|
|
SafeMemFree(pDataObjInfo);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::CreateFontCache(LPCSTR pszTridentKey)
|
|
{
|
|
VARIANTARG va;
|
|
HRESULT hr=S_OK;
|
|
IConnectionPoint *pCP;
|
|
|
|
// time to try and create a font cache. First of all, ask the host if he has one already that we should use
|
|
// if so, we're done. If not, create based on pszTridentKey. If this is NULL we use the IE regkey
|
|
|
|
if (m_pFontCache)
|
|
return S_OK;
|
|
|
|
if (m_pParentCmdTarget &&
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_FONTCACHE, 0, NULL, &va)==S_OK &&
|
|
va.vt == VT_UNKNOWN)
|
|
{
|
|
ReplaceInterface(m_pFontCache, (IFontCache *)va.punkVal);
|
|
(va.punkVal)->Release();
|
|
goto done;
|
|
}
|
|
|
|
if(g_lpIFontCache)
|
|
{
|
|
ReplaceInterface(m_pFontCache, g_lpIFontCache);
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
if (m_pFontCache &&
|
|
m_pFontCache->QueryInterface(IID_IConnectionPoint, (LPVOID *)&pCP)==S_OK)
|
|
{
|
|
pCP->Advise((IUnknown *)(IFontCacheNotify *)this, &m_dwFontCacheNotify);
|
|
pCP->Release();
|
|
}
|
|
|
|
RecalcPreivewHeight(NULL);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::DoHostProperties()
|
|
{
|
|
return m_pParentCmdTarget?m_pParentCmdTarget->Exec(NULL, OLECMDID_PROPERTIES, 0, NULL, NULL):E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::SaveAsStationery(VARIANTARG *pvaIn, VARIANTARG *pvaOut)
|
|
{
|
|
BSTR bstr=0;
|
|
LPSTREAM pstm=0,
|
|
pstmImage=0;
|
|
LPSTR lpsz,
|
|
lpszName;
|
|
TCHAR rgch[MAX_PATH],
|
|
sz[MAX_PATH+CCHMAX_STRINGRES],
|
|
rgchRes[CCHMAX_STRINGRES],
|
|
rgchExt[10];
|
|
TCHAR rgchUrl[MAX_PATH],
|
|
rgchPath[MAX_PATH];
|
|
OPENFILENAME ofn;
|
|
TCHAR szFile[MAX_PATH];
|
|
TCHAR szTitle[CCHMAX_STRINGRES];
|
|
TCHAR szFilter[100];
|
|
HRESULT hr;
|
|
LPSTR pszOpenFilePath=NULL;
|
|
WCHAR rgchW[MAX_PATH];
|
|
|
|
TraceCall("CBody::SaveAsStationery");
|
|
|
|
*rgchUrl=0;
|
|
*rgchPath = 0;
|
|
*szFile = 0;
|
|
*szFilter = 0;
|
|
|
|
LoadString(g_hLocRes, idsHtmlFileFilter, szFilter, sizeof(szFilter));
|
|
ReplaceChars(szFilter, '|', '\0');
|
|
|
|
LoadString(g_hLocRes, idsSaveAsStationery, szTitle, sizeof(szTitle));
|
|
|
|
if (pvaIn && pvaIn->vt==VT_BSTR)
|
|
{
|
|
// if we get passed in an initial path to save into, then use it
|
|
if (WideCharToMultiByte(CP_ACP, 0, (WCHAR*)pvaIn->bstrVal, -1, rgchPath, ARRAYSIZE(rgchPath), NULL, NULL))
|
|
pszOpenFilePath = rgchPath;
|
|
}
|
|
|
|
// Setup Save file struct
|
|
ZeroMemory (&ofn, sizeof (ofn));
|
|
ofn.lStructSize = sizeof (ofn);
|
|
ofn.hwndOwner = m_hwnd;
|
|
ofn.lpstrFilter = szFilter;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrFile = szFile;
|
|
ofn.nMaxFile = sizeof (szFile);
|
|
ofn.lpstrTitle = szTitle;
|
|
ofn.lpstrInitialDir = rgchPath;
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
|
|
|
|
// Show OpenFile Dialog
|
|
if (!GetSaveFileName(&ofn) || *szFile==NULL)
|
|
return MIMEEDIT_E_USERCANCEL;
|
|
|
|
StrCpyN(rgchPath, szFile, ARRAYSIZE(rgchPath));
|
|
rgchPath[ofn.nFileOffset] = NULL;
|
|
|
|
lpszName = &szFile[ofn.nFileOffset];
|
|
if (ofn.nFileExtension)
|
|
{
|
|
if(szFile[ofn.nFileExtension-1] == '.')
|
|
szFile[ofn.nFileExtension-1]=NULL;
|
|
}
|
|
|
|
if (FAILED(MimeOleCreateVirtualStream(&pstm)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
HrGetStyleTag(m_pDoc, &bstr);
|
|
|
|
pstm->Write(c_szHtml_HtmlOpenCR, lstrlen(c_szHtml_HtmlOpenCR), 0);
|
|
if (bstr)
|
|
{
|
|
pstm->Write(c_szHtml_HeadOpenCR, lstrlen(c_szHtml_HeadOpenCR), 0);
|
|
if (HrBSTRToLPSZ(CP_ACP, bstr, &lpsz)==S_OK)
|
|
{
|
|
pstm->Write(lpsz, lstrlen(lpsz), 0);
|
|
MemFree(lpsz);
|
|
}
|
|
pstm->Write(c_szHtml_HeadCloseCR, lstrlen(c_szHtml_HeadCloseCR), 0);
|
|
}
|
|
SysFreeString(bstr);
|
|
|
|
if (GetBackgroundImage(m_pDoc, &bstr)==S_OK)
|
|
{
|
|
if (HrBSTRToLPSZ(CP_ACP, bstr, &lpsz)==S_OK)
|
|
{
|
|
if (HrBindToUrl(lpsz, &pstmImage)!=S_OK)
|
|
HrFindUrlInMsg(m_pMsg, lpsz, FINDURL_SEARCH_RELATED_ONLY, &pstmImage);
|
|
|
|
if (pstmImage)
|
|
{
|
|
StrCpyN(rgchUrl, lpszName, ARRAYSIZE(rgchUrl));
|
|
|
|
// append an extension
|
|
if (HrSniffStreamFileExt(pstmImage, &lpsz)==S_OK)
|
|
{
|
|
StrCatBuff(rgchUrl, lpsz, ARRAYSIZE(rgchUrl));
|
|
SafeMimeOleFree(lpsz);
|
|
}
|
|
|
|
StrCpyN(rgch, rgchPath, ARRAYSIZE(rgch));
|
|
StrCatBuff(rgch, rgchUrl, ARRAYSIZE(rgch));
|
|
|
|
if (PathFileExists(rgch))
|
|
{
|
|
if (!LoadString(g_hLocRes, idsWarnFileExist, rgchRes, ARRAYSIZE(rgchRes)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
wnsprintf(sz, ARRAYSIZE(sz), rgchRes, rgch);
|
|
|
|
// the file exists, warn the dude
|
|
if (AthMessageBox(m_hwnd, MAKEINTRESOURCE(idsSaveAsStationery), sz, NULL, MB_YESNO|MB_DEFBUTTON2|MB_ICONEXCLAMATION )!=IDYES)
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
}
|
|
WriteStreamToFile(pstmImage, rgch, CREATE_ALWAYS, GENERIC_WRITE);
|
|
pstmImage->Release();
|
|
}
|
|
MemFree(lpsz);
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
|
|
if (*rgchUrl)
|
|
{
|
|
// output the body tag with background image
|
|
wnsprintf(rgch, ARRAYSIZE(rgch), c_szHtml_BodyOpenBgCR, rgchUrl);
|
|
pstm->Write(rgch, lstrlen(rgch), 0);
|
|
}
|
|
else
|
|
{
|
|
// reference point for BUG 31874
|
|
if (AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsSaveAsStationery), MAKEINTRESOURCEW(idsWarnBoringStationery), NULL, MB_YESNO|MB_DEFBUTTON2|MB_ICONEXCLAMATION )!=IDYES)
|
|
{
|
|
hr=MIMEEDIT_E_USERCANCEL;
|
|
goto error;
|
|
}
|
|
pstm->Write(c_szHtml_BodyOpenNbspCR, lstrlen(c_szHtml_BodyOpenNbspCR), 0);
|
|
}
|
|
|
|
pstm->Write(c_szHtml_BodyCloseCR, lstrlen(c_szHtml_BodyCloseCR), 0);
|
|
pstm->Write(c_szHtml_HtmlCloseCR, lstrlen(c_szHtml_HtmlCloseCR), 0);
|
|
|
|
StrCpyN(rgch, rgchPath, ARRAYSIZE(rgch));
|
|
StrCatBuff(rgch, lpszName, ARRAYSIZE(rgch));
|
|
StrCatBuff(rgch, ".htm", ARRAYSIZE(rgch));
|
|
WriteStreamToFile(pstm, rgch, CREATE_ALWAYS, GENERIC_WRITE);
|
|
|
|
if (pvaOut)
|
|
{
|
|
// if set, the caller wants the actual filename that was written, so convert rgch to a BSTR
|
|
pvaOut->vt = VT_BSTR;
|
|
pvaOut->bstrVal = NULL;
|
|
HrLPSZToBSTR(rgch, &pvaOut->bstrVal);
|
|
}
|
|
|
|
error:
|
|
ReleaseObj(pstm);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::TagUnreferencedImages()
|
|
{
|
|
ULONG uImage,
|
|
cImages;
|
|
IHTMLElementCollection *pCollect;
|
|
IHTMLBodyElement *pBody;
|
|
IUnknown *pUnk;
|
|
BSTR bstr;
|
|
CHAR szUrl[INTERNET_MAX_URL_LENGTH];
|
|
|
|
// BUG: we can't use the image collection, as it shows images up multiple times
|
|
// for NAV compatibility. Have to filter the 'all' collection on "IMG" tags.
|
|
|
|
if (HrGetCollectionOf(m_pDoc, (BSTR)c_bstr_IMG, &pCollect)==S_OK)
|
|
{
|
|
cImages = UlGetCollectionCount(pCollect);
|
|
|
|
for (uImage=0; uImage<cImages; uImage++)
|
|
{
|
|
if (HrGetCollectionItem(pCollect, uImage, IID_IUnknown, (LPVOID *)&pUnk)==S_OK)
|
|
{
|
|
if (HrGetMember(pUnk, (BSTR)c_bstr_SRC, VARIANT_FALSE, &bstr)==S_OK)
|
|
{
|
|
if (WideCharToMultiByte(CP_ACP, 0, bstr, -1, szUrl, INTERNET_MAX_URL_LENGTH, NULL, NULL) &&
|
|
HrFindUrlInMsg(m_pMsg, szUrl, FINDURL_SEARCH_RELATED_ONLY, NULL)!=S_OK)
|
|
{
|
|
// this URL was not in the message, let's tag it as a NOSEND url
|
|
HrSetMember(pUnk, (BSTR)c_bstr_NOSEND, (BSTR)c_bstr_1);
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
pUnk->Release();
|
|
}
|
|
}
|
|
pCollect->Release();
|
|
}
|
|
|
|
// if the background is not included tag as NOSEND
|
|
if (!FAILED(GetBodyElement(&pBody)))
|
|
{
|
|
if (!FAILED(GetBackgroundImage(m_pDoc, &bstr)))
|
|
{
|
|
if (WideCharToMultiByte(CP_ACP, 0, bstr, -1, szUrl, INTERNET_MAX_URL_LENGTH, NULL, NULL) &&
|
|
HrFindUrlInMsg(m_pMsg, szUrl, FINDURL_SEARCH_RELATED_ONLY, NULL)!=S_OK)
|
|
{
|
|
HrSetMember(pBody, (BSTR)c_bstr_NOSEND, (BSTR)c_bstr_1);
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
pBody->Release();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::FindFrameDownwards(LPCWSTR pszTargetName, DWORD dwFlags, IUnknown **ppunkTargetFrame)
|
|
{
|
|
if (ppunkTargetFrame)
|
|
*ppunkTargetFrame=NULL;
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CBody::FindFrameInContext(LPCWSTR pszTargetName, IUnknown *punkContextFrame, DWORD dwFlags, IUnknown **ppunkTargetFrame)
|
|
{
|
|
return DoFindFrameInContext(m_lpOleObj, (IUnknown *)(IPropertyNotifySink *)this,
|
|
pszTargetName, punkContextFrame, dwFlags, ppunkTargetFrame);
|
|
}
|
|
|
|
HRESULT CBody::OnChildFrameActivate(IUnknown *pUnkChildFrame)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::OnChildFrameDeactivate(IUnknown *pUnkChildFrame)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::NavigateHack(DWORD grfHLNF,LPBC pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CBody::FindBrowserByIndex(DWORD dwID,IUnknown **ppunkBrowser)
|
|
{
|
|
if (ppunkBrowser)
|
|
*ppunkBrowser=NULL;
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
HRESULT CBody::ApplyDocumentVerb(VARIANTARG *pvaIn)
|
|
{
|
|
HRESULT hr;
|
|
IHTMLDocument2 *pDoc=0;
|
|
|
|
TraceCall("CBody::ApplyDocumentVerb");
|
|
|
|
if(pvaIn && pvaIn->vt==VT_UNKNOWN && pvaIn->punkVal)
|
|
pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, (LPVOID *)&pDoc);
|
|
|
|
hr = ApplyDocument(pDoc);
|
|
ReleaseObj(pDoc);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::ApplyDocument(IHTMLDocument2 *pDocStationery)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceCall("CBody::ApplyDocument");
|
|
|
|
if(pDocStationery)
|
|
{
|
|
hr = HrCopyStyleSheets(pDocStationery, m_pDoc);
|
|
if (!FAILED(hr))
|
|
hr = HrCopyBackground(pDocStationery, m_pDoc);
|
|
}
|
|
else
|
|
{
|
|
hr = HrRemoveStyleSheets(m_pDoc);
|
|
if (!FAILED(hr))
|
|
hr = HrRemoveBackground(m_pDoc);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::OnWMCreate()
|
|
{
|
|
RecalcPreivewHeight(NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
static const TBBUTTON g_rgBtnBarButtons[] = {
|
|
{ itbBadSign, idmPaneBadSigning, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 2 },
|
|
{ itbSigning, idmPaneSigning, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 0 },
|
|
{ itbEncryption,idmPaneEncryption, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 1 },
|
|
{ itbBadEnc, idmPaneBadEncryption, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 3 },
|
|
{ itbVCard, idmPaneVCard, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 4 },
|
|
{ itbPaperclip, idmPanePaperclip, TBSTATE_ENABLED, TBSTYLE_DROPDOWN, {0,0}, 0, 5 },
|
|
};
|
|
|
|
|
|
HRESULT CBody::InitToolbar()
|
|
{
|
|
TCHAR rgch[CCHMAX_STRINGRES];
|
|
DWORD dwBtnSize;
|
|
|
|
if (!m_hwndBtnBar)
|
|
{
|
|
Assert (!m_hIml);
|
|
Assert (!m_hImlHot);
|
|
|
|
m_hwndBtnBar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
|
|
TBSTYLE_TRANSPARENT|TBSTYLE_FLAT|TBSTYLE_TOOLTIPS|WS_CHILD|WS_CLIPSIBLINGS|CCS_NODIVIDER|CCS_NORESIZE|CCS_NOPARENTALIGN,
|
|
0, 0, 32, 100, m_hwnd, (HMENU)idcBtnBar, g_hInst, NULL);
|
|
|
|
if (!m_hwndBtnBar)
|
|
return E_FAIL;
|
|
|
|
m_hIml = ImageList_LoadImage(g_hLocRes, MAKEINTRESOURCE(idbPaneBar32), CX_PANEICON, 0, RGB(255, 0, 255), IMAGE_BITMAP, LR_LOADMAP3DCOLORS|LR_CREATEDIBSECTION);
|
|
if (!m_hIml)
|
|
return E_OUTOFMEMORY;
|
|
|
|
SendMessage(m_hwndBtnBar, TB_SETIMAGELIST, 0, (LPARAM)m_hIml);
|
|
|
|
m_hImlHot = ImageList_LoadImage(g_hLocRes, MAKEINTRESOURCE(idbPaneBar32Hot), CX_PANEICON, 0, RGB(255, 0, 255), IMAGE_BITMAP, LR_LOADMAP3DCOLORS|LR_CREATEDIBSECTION);
|
|
if (!m_hImlHot)
|
|
return E_OUTOFMEMORY;
|
|
|
|
SendMessage(m_hwndBtnBar, TB_SETHOTIMAGELIST, 0, (LPARAM)m_hImlHot);
|
|
SendMessage(m_hwndBtnBar, TB_SETMAXTEXTROWS, 0, 0L);
|
|
SendMessage(m_hwndBtnBar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
|
|
SendMessage(m_hwndBtnBar, TB_ADDBUTTONS, ARRAYSIZE(g_rgBtnBarButtons), (LPARAM)g_rgBtnBarButtons);
|
|
if (LoadString(g_hLocRes, idsBtnBarTTList, rgch, ARRAYSIZE(rgch)))
|
|
{
|
|
ReplaceChars(rgch, '|', '\0');
|
|
SendMessage(m_hwndBtnBar, TB_ADDSTRING, 0, (LPARAM)rgch);
|
|
}
|
|
|
|
m_cVisibleBtns = 0;
|
|
// set the initial height of the toolbar, based on the button size
|
|
dwBtnSize = (DWORD) SendMessage(m_hwndBtnBar, TB_GETBUTTONSIZE, 0, 0);
|
|
SetWindowPos(m_hwndBtnBar, NULL, 0, 0, 0, HIWORD(dwBtnSize), SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_NOMOVE);
|
|
// now that we have setup the button height, recalc the preview header height
|
|
RecalcPreivewHeight(NULL);
|
|
Resize();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::UpdateButtons()
|
|
{
|
|
ULONG dwBtnSize,
|
|
uBtn;
|
|
OLECMD rgSecureCmds[]={{OECSECCMD_ENCRYPTED, 0},
|
|
{OECSECCMD_SIGNED, 0}};
|
|
RECT rc;
|
|
int cxBar;
|
|
|
|
Assert (m_hwndBtnBar);
|
|
|
|
// hide all the buttons
|
|
m_cVisibleBtns=0;
|
|
for (uBtn = 0; uBtn < ARRAYSIZE(g_rgBtnBarButtons); uBtn++)
|
|
SendMessage(m_hwndBtnBar, TB_HIDEBUTTON, g_rgBtnBarButtons[uBtn].idCommand, MAKELONG(TRUE, 0));
|
|
|
|
// turn on the applicable buttons.
|
|
if (m_pAttMenu && m_pAttMenu->HasAttach()==S_OK)
|
|
{
|
|
SendMessage(m_hwndBtnBar, TB_HIDEBUTTON, idmPanePaperclip, MAKELONG(FALSE, 0));
|
|
m_cVisibleBtns++;
|
|
}
|
|
|
|
if (m_pAttMenu && m_pAttMenu->HasVCard()==S_OK)
|
|
{
|
|
SendMessage(m_hwndBtnBar, TB_HIDEBUTTON, idmPaneVCard, MAKELONG(FALSE, 0));
|
|
m_cVisibleBtns++;
|
|
}
|
|
|
|
// see if the host supports our private S/Mime functionality
|
|
// we query the host for the security state of the message, this can take one of 3 forms for both
|
|
// signed and ecrypted: (none, good or bad) represented by (OLECMDF_INVISIBLE, OLECMDF_ENABLED, and OLECMDF_DISABLED)
|
|
// respectivley
|
|
if (m_pParentCmdTarget)
|
|
{
|
|
if (m_pParentCmdTarget->QueryStatus(&CMDSETID_OESecurity, ARRAYSIZE(rgSecureCmds), rgSecureCmds, NULL)==S_OK)
|
|
{
|
|
if (rgSecureCmds[0].cmdf & OLECMDF_SUPPORTED && !(rgSecureCmds[0].cmdf & OLECMDF_INVISIBLE))
|
|
{
|
|
SendMessage(m_hwndBtnBar, TB_HIDEBUTTON, rgSecureCmds[0].cmdf & OLECMDF_ENABLED ? idmPaneEncryption : idmPaneBadEncryption, MAKELONG(FALSE, 0));
|
|
m_cVisibleBtns++;
|
|
}
|
|
|
|
if (rgSecureCmds[1].cmdf & OLECMDF_SUPPORTED && !(rgSecureCmds[1].cmdf & OLECMDF_INVISIBLE))
|
|
{
|
|
SendMessage(m_hwndBtnBar, TB_HIDEBUTTON, rgSecureCmds[1].cmdf & OLECMDF_ENABLED ? idmPaneSigning : idmPaneBadSigning, MAKELONG(FALSE, 0));
|
|
m_cVisibleBtns++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// size the toolbar based on the number of visible buttons
|
|
dwBtnSize = (DWORD) SendMessage(m_hwndBtnBar, TB_GETBUTTONSIZE, 0, 0);
|
|
cxBar = LOWORD(dwBtnSize)*m_cVisibleBtns;
|
|
GetClientRect(m_hwnd, &rc);
|
|
AssertSz(m_cyPreview >= HIWORD(dwBtnSize), "preview header is too small for the button bar");
|
|
SetWindowPos(m_hwndBtnBar, NULL, rc.right - cxBar, (m_cyPreview - HIWORD(dwBtnSize))/2, LOWORD(dwBtnSize)*m_cVisibleBtns, HIWORD(dwBtnSize),SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER|SWP_SHOWWINDOW);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::ShowAttachMenu(BOOL fRightClick)
|
|
{
|
|
POINT pt;
|
|
|
|
PointFromButton(idmPanePaperclip, &pt);
|
|
|
|
if (m_pAttMenu && m_pAttMenu->HasAttach()==S_OK)
|
|
m_pAttMenu->Show(m_hwnd, &pt, fRightClick);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::ShowPreview(BOOL fOn)
|
|
{
|
|
if (fOn)
|
|
{
|
|
UpdatePreviewLabels();
|
|
|
|
// if user turns on the preview pane after loaded, then defer-create the attachment menu
|
|
if (m_fMessageParsed)
|
|
UpdateBtnBar();
|
|
|
|
}
|
|
|
|
if (m_hwndBtnBar)
|
|
ShowWindow(m_hwndBtnBar, fOn?SW_SHOW:SW_HIDE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::PointFromButton(int idm, POINT *ppt)
|
|
{
|
|
RECT rc;
|
|
|
|
if (!SendMessage(m_hwndBtnBar, TB_GETRECT, idm, (LPARAM)&rc))
|
|
return E_FAIL;
|
|
|
|
ppt->x = rc.right;
|
|
ppt->y = rc.bottom;
|
|
ClientToScreen(m_hwndBtnBar, ppt);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::UpdateBtnBar()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// create the attachment menu. This will be destroyed on every load/unload to reflect the new state of the
|
|
// message
|
|
if (!m_pAttMenu && m_pMsg)
|
|
{
|
|
hr = EnsureAttMenu();
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
// make sure the button bar is created. This will be created once per preview pane
|
|
hr = InitToolbar();
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
hr = UpdateButtons();
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::EnsureAttMenu()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pAttMenu && m_pMsg)
|
|
{
|
|
m_pAttMenu = new CAttMenu();
|
|
if (!m_pAttMenu)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = m_pAttMenu->Init(m_pMsg, m_pFontCache, m_pParentInPlaceFrame, m_pParentCmdTarget);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT HrSniffUrlForRfc822(LPWSTR pszUrlW)
|
|
{
|
|
LPWSTR lpszW;
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (!FAILED(FindMimeFromData(NULL, pszUrlW, NULL, NULL, NULL, NULL, &lpszW, 0)))
|
|
{
|
|
if (StrCmpW(lpszW, L"message/rfc822")==0)
|
|
hr = S_OK;
|
|
CoTaskMemFree(lpszW);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBody::SaveAttachments()
|
|
{
|
|
return SaveAttachmentsWithPath(m_hwnd, m_pParentCmdTarget, m_pMsg);
|
|
}
|
|
|
|
HRESULT CBody::InsertBackgroundSound()
|
|
{
|
|
ULONG cRepeat;
|
|
BSTR bstrUrl;
|
|
BGSOUNDDLG rBGSound;
|
|
|
|
rBGSound.wszUrl[0]=0; // null string
|
|
rBGSound.cRepeat = 1; // default to 1 repeat
|
|
|
|
if (GetBackgroundSound(m_pDoc, &rBGSound.cRepeat, &bstrUrl)==S_OK)
|
|
{
|
|
StrCpyNW(rBGSound.wszUrl, bstrUrl, ARRAYSIZE(rBGSound.wszUrl));
|
|
SysFreeString(bstrUrl);
|
|
}
|
|
|
|
if (S_OK == DoBackgroundSoundDlg(m_hwnd, &rBGSound))
|
|
{
|
|
bstrUrl = SysAllocString(rBGSound.wszUrl);
|
|
if (bstrUrl)
|
|
{
|
|
SetBackgroundSound(m_pDoc, rBGSound.cRepeat, bstrUrl);
|
|
SysFreeString(bstrUrl);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::EnableSounds(BOOL fOn)
|
|
{
|
|
VARIANTARG va;
|
|
|
|
va.vt = VT_I4;
|
|
va.lVal = fOn;
|
|
if (m_pCmdTarget)
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_ENABLE_INTERACTION, NULL, &va, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::ShowSourceTabs(BOOL fOn)
|
|
{
|
|
TC_ITEM tci;
|
|
HFONT hFont;
|
|
TCHAR rgch[CCHMAX_STRINGRES];
|
|
WNDCLASSEX wc;
|
|
int i;
|
|
|
|
if (fOn == m_fSrcTabs)
|
|
return S_OK;
|
|
|
|
if (!fOn && m_hwndTab)
|
|
{
|
|
// if turning off, make sure we got back to edit-mode
|
|
SetSourceTabs(MEST_EDIT);
|
|
}
|
|
|
|
if (m_hwndTab)
|
|
{ // already created
|
|
ShowWindow(m_hwndTab, fOn?SW_SHOW:SW_HIDE);
|
|
goto exit;
|
|
}
|
|
|
|
|
|
m_hwndTab = CreateWindowEx(0,
|
|
WC_TABCONTROL,
|
|
NULL,
|
|
WS_CHILD|WS_TABSTOP|WS_VISIBLE|TCS_BOTTOM|TCS_FIXEDWIDTH,
|
|
0, 0, 0, 0,
|
|
m_hwnd,
|
|
(HMENU)idcTabs,
|
|
g_hLocRes,
|
|
NULL);
|
|
if (!m_hwndTab)
|
|
return E_FAIL;
|
|
|
|
tci.mask = TCIF_TEXT;
|
|
for (i=MEST_EDIT; i<=MEST_PREVIEW; i++)
|
|
{
|
|
LoadString(g_hLocRes, idsEditTab+i, rgch, ARRAYSIZE(rgch));
|
|
tci.pszText = rgch;
|
|
TabCtrl_InsertItem(m_hwndTab, i, &tci);
|
|
}
|
|
|
|
if (m_pFontCache &&
|
|
m_pFontCache->GetFont(FNT_SYS_ICON, NULL, &hFont)==S_OK)
|
|
SendMessage(m_hwndTab, WM_SETFONT, (WPARAM)hFont, 0);
|
|
|
|
exit:
|
|
m_fSrcTabs = fOn;
|
|
Resize();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::ShowSourceView(ULONG uSrcView)
|
|
{
|
|
IStream *pstm;
|
|
BOOL fFocus;
|
|
HRESULT hr=S_OK;
|
|
|
|
if (m_uSrcView == uSrcView) // noop
|
|
return S_OK;
|
|
|
|
/* store information about the current state. we care about
|
|
- who has focus
|
|
- caching the IStream of 'current' HTML
|
|
- dirty states
|
|
*/
|
|
switch (m_uSrcView)
|
|
{
|
|
case MEST_EDIT:
|
|
// if switching away from edit-mode remember the dirty state
|
|
m_fWasDirty = IsDirty() == S_OK;
|
|
fFocus = m_fUIActive;
|
|
SafeRelease(m_pstmHtmlSrc);
|
|
GetBodyStream(m_pDoc, TRUE, &m_pstmHtmlSrc);
|
|
break;
|
|
|
|
case MEST_PREVIEW:
|
|
// m_pstmHtml is not saved from this mode, assume the previous setting
|
|
AssertSz(m_pstmHtmlSrc, "This should be set from a previous switch");
|
|
fFocus = m_fUIActive;
|
|
break;
|
|
|
|
case MEST_SOURCE:
|
|
// if switching away from source-mode remember the dirty state
|
|
Assert (m_pSrcView);
|
|
SafeRelease(m_pstmHtmlSrc);
|
|
m_pSrcView->Save(&m_pstmHtmlSrc);
|
|
m_fWasDirty = m_pSrcView->IsDirty() == S_OK;
|
|
fFocus = m_pSrcView->HasFocus()==S_OK;
|
|
break;
|
|
}
|
|
|
|
|
|
m_pDocView->UIActivate(FALSE);
|
|
|
|
// at this point m_pstmSrcHtml contains the new HTML source
|
|
switch (uSrcView)
|
|
{
|
|
case MEST_EDIT:
|
|
/* when switching to edit mode.
|
|
- reload trident
|
|
- ensure design-mode
|
|
- restore focus
|
|
- restore dirty state
|
|
- hide source-view (if shown)
|
|
*/
|
|
SetDesignMode(TRUE);
|
|
_ReloadWithHtmlSrc(m_pstmHtmlSrc);
|
|
HrSetDirtyFlagImpl(m_pDoc, !!m_fWasDirty);
|
|
if (fFocus)
|
|
m_pDocView->UIActivate(TRUE);
|
|
if (m_pSrcView)
|
|
m_pSrcView->Show(FALSE, FALSE);
|
|
m_pDocView->Show(TRUE);
|
|
break;
|
|
|
|
case MEST_PREVIEW:
|
|
/* when switching to preview mode.
|
|
- reload trident
|
|
- ensure design-mode is OFF
|
|
- restore focus
|
|
- restore dirty state
|
|
- hide source-view (if shown)
|
|
*/
|
|
SetDesignMode(FALSE);
|
|
_ReloadWithHtmlSrc(m_pstmHtmlSrc);
|
|
if (fFocus)
|
|
m_pDocView->UIActivate(TRUE);
|
|
if (m_pSrcView)
|
|
m_pSrcView->Show(FALSE, FALSE);
|
|
m_pDocView->Show(TRUE);
|
|
break;
|
|
|
|
case MEST_SOURCE:
|
|
/* when switching to source mode.
|
|
- defer-create the source view window (if needed)
|
|
- hide trident
|
|
- restore focus
|
|
- restore dirty state */
|
|
|
|
hr = _EnsureSrcView();
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
Assert (m_pSrcView);
|
|
m_pSrcView->Show(TRUE, IsColorSourceEditing()==S_OK);
|
|
m_pSrcView->Load(m_pstmHtmlSrc);
|
|
m_pSrcView->SetDirty(m_fWasDirty);
|
|
m_pDocView->Show(FALSE);
|
|
// restore focus
|
|
if (fFocus)
|
|
m_pSrcView->SetFocus();
|
|
break;
|
|
}
|
|
|
|
ShowFormatBar(uSrcView == MEST_EDIT && m_uHdrStyle == MESTYLE_FORMATBAR);
|
|
m_uSrcView = uSrcView;
|
|
Resize();
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CBody::SetSourceTabs(ULONG ulTab)
|
|
{
|
|
int rgNext[3] = {MEST_SOURCE, MEST_PREVIEW, MEST_EDIT},
|
|
rgPrev[3] = {MEST_PREVIEW, MEST_EDIT, MEST_SOURCE};
|
|
|
|
if (!m_fSrcTabs) // do nothing if not in source-tab-mode
|
|
return E_UNEXPECTED;
|
|
|
|
switch (ulTab)
|
|
{
|
|
case MEST_NEXT:
|
|
ulTab = rgNext[m_uSrcView];
|
|
break;
|
|
|
|
case MEST_PREVIOUS:
|
|
ulTab = rgPrev[m_uSrcView];
|
|
break;
|
|
|
|
case MEST_EDIT:
|
|
case MEST_SOURCE:
|
|
case MEST_PREVIEW:
|
|
break;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Assert (((int) ulTab) >= MEST_EDIT && ((int) ulTab) <= MEST_PREVIEW);
|
|
ShowSourceView(ulTab);
|
|
TabCtrl_SetCurSel(m_hwndTab, ulTab);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::IsColorSourceEditing()
|
|
{
|
|
VARIANTARG va;
|
|
|
|
if (m_pParentCmdTarget &&
|
|
m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SOURCEEDIT_FLAGS, 0, NULL, &va)==S_OK &&
|
|
va.vt == VT_I4 &&
|
|
!(va.lVal & MESRCFLAGS_COLOR))
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::HrCreateSpeller(BOOL fBkgrnd)
|
|
{
|
|
HRESULT hr;
|
|
VARIANTARG va;
|
|
|
|
if (m_pSpell)
|
|
return NOERROR;
|
|
|
|
#ifndef BACKGROUNDSPELL
|
|
// just to make sure background spelling is disabled
|
|
Assert(!fBkgrnd);
|
|
fBkgrnd = FALSE;
|
|
#endif // !BACKGROUNDSPELL
|
|
|
|
if (m_fDesignMode && m_pDoc && m_pParentCmdTarget && FCheckSpellAvail(m_pParentCmdTarget) &&
|
|
SUCCEEDED(m_pParentCmdTarget->Exec(&CMDSETID_MimeEditHost, MEHOSTCMDID_SPELL_OPTIONS, OLECMDEXECOPT_DODEFAULT, NULL, &va)))
|
|
{
|
|
if (((V_I4(&va) & MESPELLOPT_CHECKONTYPE) && fBkgrnd) || !fBkgrnd)
|
|
m_pSpell = new CSpell(m_pDoc, m_pParentCmdTarget, V_I4(&va));
|
|
}
|
|
|
|
if (m_pSpell)
|
|
{
|
|
if (!m_pSpell->OpenSpeller())
|
|
{
|
|
SafeRelease(m_pSpell);
|
|
return E_FAIL;
|
|
}
|
|
|
|
#ifdef BACKGROUNDSPELL
|
|
if (fBkgrnd)
|
|
{
|
|
SetTimer(m_hwnd, idTimerBkgrndSpell, BKGRNDSPELL_TICKTIME, NULL);
|
|
m_pSpell->HrRegisterKeyPressNotify(TRUE);
|
|
m_fBkgrndSpelling = TRUE;
|
|
}
|
|
#endif // BACKGROUNDSPELL
|
|
}
|
|
|
|
return m_pSpell ? NOERROR : E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CBody::OnPreFontChange()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::OnPostFontChange()
|
|
{
|
|
HFONT hFont;
|
|
|
|
RecalcPreivewHeight(NULL);
|
|
Resize();
|
|
|
|
// update the tabfont
|
|
if (m_hwndTab &&
|
|
m_pFontCache &&
|
|
m_pFontCache->GetFont(FNT_SYS_ICON, NULL, &hFont)==S_OK)
|
|
SendMessage(m_hwndTab, WM_SETFONT, (WPARAM)hFont, 0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
extern BOOL g_fCanEditBiDi;
|
|
HRESULT CBody::HrFormatParagraph()
|
|
{
|
|
OLECMD rgCmds[]= { {IDM_JUSTIFYLEFT, 0}, // careful about ordering!!
|
|
{IDM_JUSTIFYRIGHT, 0},
|
|
{IDM_JUSTIFYCENTER, 0},
|
|
{IDM_JUSTIFYFULL, 0},
|
|
{IDM_ORDERLIST, 0},
|
|
{IDM_UNORDERLIST, 0},
|
|
{IDM_BLOCKDIRLTR, 0},
|
|
{IDM_BLOCKDIRRTL, 0}};
|
|
|
|
int rgidm[] = { idmFmtLeft, // careful about ordering!!
|
|
idmFmtRight,
|
|
idmFmtCenter,
|
|
idmFmtJustify,
|
|
idmFmtNumbers,
|
|
idmFmtBullets,
|
|
idmFmtBlockDirLTR,
|
|
idmFmtBlockDirRTL};
|
|
PARAPROP ParaProp;
|
|
int i;
|
|
if(FAILED(m_pCmdTarget->QueryStatus(&CMDSETID_Forms3, sizeof(rgCmds)/sizeof(OLECMD), rgCmds, NULL)))
|
|
return E_FAIL;
|
|
memset(&ParaProp, 0, sizeof(PARAPROP));
|
|
|
|
for(i = 0; i < 4; i++)
|
|
if(rgCmds[i].cmdf&OLECMDF_LATCHED)
|
|
ParaProp.group[0].iID=rgidm[i];
|
|
|
|
ParaProp.group[1].iID = idmFmtBulletsNone;
|
|
|
|
for(i = 4; i < 6; i++)
|
|
if(rgCmds[i].cmdf&OLECMDF_LATCHED)
|
|
ParaProp.group[1].iID=rgidm[i];
|
|
|
|
for(i = 6; i < 8; i++)
|
|
if(rgCmds[i].cmdf&OLECMDF_LATCHED)
|
|
ParaProp.group[2].iID=rgidm[i];
|
|
|
|
if(DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddFormatPara), m_hwnd, FmtParaDlgProc, (LPARAM)&ParaProp)==IDOK)
|
|
{
|
|
// Dir attribute implies direction.
|
|
// lets change it first
|
|
for(i = g_fCanEditBiDi? 2 : 1; i > -1; i--)
|
|
if(ParaProp.group[i].bChanged)
|
|
{
|
|
OnWMCommand(m_hwnd, ParaProp.group[i].iID, 0);
|
|
}
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------
|
|
//
|
|
// Member: SearchForCIDUrls
|
|
//
|
|
// Synopsis: added to support MSPHONE. They send documents with
|
|
// multipart/related CID:foo.wav files, the URLs are renderd
|
|
// using an Active-Movive control embedded in the HTML.
|
|
// Trident never fires pluggable protocol requests for the
|
|
// urls and so we don't flag the attachments as rendered. Here we
|
|
// walk the document and try and find any CID's that are referenced
|
|
// in the HTML. APP requests get fired when the page goes oncomplete and
|
|
// the activex controls are activated.
|
|
//---------------------------------------------------------------
|
|
HRESULT CBody::SearchForCIDUrls()
|
|
{
|
|
HBODY hBody;
|
|
IMimeEditTagCollection *pCollect;
|
|
ULONG cFetched;
|
|
IMimeEditTag *pTag;
|
|
BSTR bstrSrc;
|
|
LPSTR pszUrlA,
|
|
pszBodyA;
|
|
|
|
// nothing todo if there is no multipart/related section
|
|
if (m_pMsg == NULL ||
|
|
MimeOleGetRelatedSection(m_pMsg, FALSE, &hBody, NULL)!=S_OK)
|
|
return S_OK;
|
|
|
|
// active-movie controls (for MSPHONE)
|
|
if (CreateActiveMovieCollection(m_pDoc, &pCollect)==S_OK)
|
|
{
|
|
pCollect->Reset();
|
|
|
|
while (pCollect->Next(1, &pTag, &cFetched)==S_OK && cFetched==1)
|
|
{
|
|
if (pTag->GetSrc(&bstrSrc)==S_OK)
|
|
{
|
|
pszUrlA = PszToANSI(CP_ACP, bstrSrc);
|
|
if (pszUrlA)
|
|
{
|
|
// if it's an MHTML: url then we have to fixup to get the cid:
|
|
// as ResolveURL won't recognize it
|
|
if (StrCmpNIA(pszUrlA, "mhtml:", 6)==0)
|
|
{
|
|
if (!FAILED(MimeOleParseMhtmlUrl(pszUrlA, NULL, &pszBodyA)))
|
|
{
|
|
// pszBody pszUrlA is guarnteed to be smaller
|
|
StrCpyN(pszUrlA, pszBodyA, lstrlen(pszUrlA)+1);
|
|
SafeMimeOleFree(pszBodyA);
|
|
}
|
|
}
|
|
m_pMsg->ResolveURL(NULL, NULL, pszUrlA, URL_RESOLVE_RENDERED, NULL);
|
|
MemFree(pszUrlA);
|
|
}
|
|
SysFreeString(bstrSrc);
|
|
}
|
|
pTag->Release();
|
|
}
|
|
pCollect->Release();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBody::_ReloadWithHtmlSrc(IStream *pstm)
|
|
{
|
|
IMimeMessage *pMsg;
|
|
|
|
// if we're currently in HTML view, save the changes and reload trident
|
|
if (m_pMsg && pstm)
|
|
{
|
|
pMsg = m_pMsg;
|
|
pMsg->AddRef();
|
|
pMsg->SetTextBody(TXT_HTML, IET_INETCSET, NULL, pstm, NULL);
|
|
m_fReloadingSrc = TRUE;
|
|
Load(pMsg);
|
|
m_fLoading = FALSE;
|
|
pMsg->Release();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CBody::_EnsureSrcView()
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
if (!m_pSrcView)
|
|
{
|
|
m_pSrcView = new CMsgSource();
|
|
if (!m_pSrcView)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
hr = m_pSrcView->Init(m_hwnd, idcSrcEdit, (IOleCommandTarget *)this);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
}
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Function: _OnSaveImage
|
|
*
|
|
* Purpose:
|
|
* if we're doing a SaveAs on an image, trident will not show the correct options in the save-as dialog
|
|
* unless the image is in the cache. For auto-inlined images, we changed the behaviour for OE5 to show
|
|
* them thro' the pluggable protocol. This is a hack to preload the cache with the pluggable protocol to
|
|
* the image. Note that we also need to delete the cache entry as the full URL is not persitable across sessions.
|
|
* ie: given the mhtml://mid:xxxxx!foobar.gif the mid: generated number is reused across OE sessions.
|
|
*
|
|
*/
|
|
|
|
HRESULT CBody::_OnSaveImage()
|
|
{
|
|
IHTMLImgElement *pImg;
|
|
BSTR bstr=NULL;
|
|
LPSTR pszUrlA=NULL;
|
|
LPSTREAM pstm=NULL;
|
|
HRESULT hrCached=E_FAIL;
|
|
|
|
// try and get the image URL (m_pDispContext points to the object the context menu is acting on)
|
|
if (m_pDispContext &&
|
|
m_pDispContext->QueryInterface(IID_IHTMLImgElement, (LPVOID *)&pImg)==S_OK)
|
|
{
|
|
pImg->get_src(&bstr);
|
|
if (bstr)
|
|
{
|
|
pszUrlA = PszToANSI(CP_ACP, bstr);
|
|
SysFreeString(bstr);
|
|
}
|
|
pImg->Release();
|
|
}
|
|
|
|
// if this URL is in the mutlipart/related secion
|
|
if (!FAILED(HrFindUrlInMsg(m_pMsg, pszUrlA, 0, &pstm)))
|
|
{
|
|
DeleteUrlCacheEntryA(pszUrlA);
|
|
hrCached = CreateCacheFileFromStream(pszUrlA, pstm);
|
|
pstm->Release();
|
|
}
|
|
|
|
|
|
Assert (m_pCmdTarget);
|
|
m_pCmdTarget->Exec(&CMDSETID_Forms3, IDM_SAVEPICTURE, OLECMDEXECOPT_DODEFAULT, NULL, NULL);
|
|
|
|
// if we successfully cached this URL, be sure to remove it once the save is complete.
|
|
if (SUCCEEDED(hrCached))
|
|
DeleteUrlCacheEntryA(pszUrlA);
|
|
|
|
MemFree(pszUrlA);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
CVerHost::CVerHost()
|
|
{
|
|
m_cRef=1;
|
|
}
|
|
|
|
|
|
CVerHost::~CVerHost()
|
|
{
|
|
}
|
|
|
|
|
|
HRESULT CVerHost::QueryInterface(REFIID riid, LPVOID FAR *lplpObj)
|
|
{
|
|
if(!lplpObj)
|
|
return E_INVALIDARG;
|
|
|
|
*lplpObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IVersionHost))
|
|
*lplpObj = (IVersionHost *) this;
|
|
else
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
ULONG CVerHost::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
|
|
ULONG CVerHost::Release()
|
|
{
|
|
if (--m_cRef==0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
HRESULT CVerHost::QueryUseLocalVersionVector(BOOL *fUseLocal)
|
|
{
|
|
*fUseLocal = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CVerHost::QueryVersionVector(IVersionVector *pVersion)
|
|
{
|
|
|
|
if (pVersion == NULL)
|
|
return E_INVALIDARG;
|
|
|
|
return pVersion->SetVersion(L"VML", NULL);
|
|
}
|
|
|