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.
1257 lines
32 KiB
1257 lines
32 KiB
#include <pch.hxx>
|
|
#include "dllmain.h"
|
|
#include "demand.h"
|
|
#include "resource.h"
|
|
#include "viewsrc.h"
|
|
#include "util.h"
|
|
|
|
#define idTimerEditChange 401
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Parsing constants
|
|
|
|
// Ignore all <'s and >'s in the following environments:
|
|
// Script <script> here </script>
|
|
// Denali <% here %>
|
|
// Comment <!-- here -->
|
|
// String literal < ... "here" ... >
|
|
// (as tag attribute) < ... 'here' ... >
|
|
|
|
static enum
|
|
{
|
|
ENV_NORMAL = 0, // normal
|
|
ENV_COMMENT = 1, // ignore <'s and >'s
|
|
ENV_QUOTE = 2, // " "
|
|
ENV_SCRIPT = 3, // " "
|
|
ENV_DENALI = 4, // " "
|
|
ENV_QUOTE_SCR= 5, // " "; string literal in SCRIPT tag
|
|
};
|
|
|
|
static const char QUOTE_1 = '\'';
|
|
static const char QUOTE_2 = '\"';
|
|
|
|
|
|
HRESULT CALLBACK FreeViewSrcDataObj(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 ViewSource(HWND hwndParent, IMimeMessage *pMsg)
|
|
{
|
|
CViewSource *pViewSrc=0;
|
|
HRESULT hr;
|
|
|
|
TraceCall("MimeEditViewSource");
|
|
|
|
if (!DemandLoadRichEdit())
|
|
return TraceResult(MIMEEDIT_E_LOADLIBRARYFAILURE);
|
|
|
|
|
|
pViewSrc = new CViewSource();
|
|
if (!pViewSrc)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = pViewSrc->Init(hwndParent, pMsg);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
|
|
hr = pViewSrc->Show();
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
|
|
// pViewSrc will maintain it's own refcount and self-destruct on close
|
|
exit:
|
|
ReleaseObj(pViewSrc);
|
|
return hr;
|
|
}
|
|
|
|
|
|
CViewSource::CViewSource()
|
|
{
|
|
m_hwnd = NULL;
|
|
m_hwndEdit = NULL;
|
|
m_pMsg = NULL;
|
|
m_cRef = 1;
|
|
}
|
|
|
|
|
|
CViewSource::~CViewSource()
|
|
{
|
|
SafeRelease(m_pMsg);
|
|
}
|
|
|
|
|
|
ULONG CViewSource::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
ULONG CViewSource::Release()
|
|
{
|
|
if (--m_cRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
HRESULT CViewSource::Init(HWND hwndParent, IMimeMessage *pMsg)
|
|
{
|
|
ReplaceInterface(m_pMsg, pMsg);
|
|
|
|
if (!CreateDialogParam(g_hLocRes, MAKEINTRESOURCE(iddMsgSource), hwndParent, CViewSource::_ExtDlgProc, (LPARAM)this))
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CViewSource::Show()
|
|
{
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
INT_PTR CALLBACK CViewSource::_ExtDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CViewSource *pThis = (CViewSource *)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
if (msg == WM_INITDIALOG)
|
|
{
|
|
pThis = (CViewSource *)lParam;
|
|
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
|
|
}
|
|
|
|
return pThis ? pThis->_DlgProc(hwnd, msg, wParam, lParam) : FALSE;
|
|
}
|
|
|
|
|
|
INT_PTR CViewSource::_DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPSTREAM pstm;
|
|
CHARFORMAT cf;
|
|
BODYOFFSETS rOffset;
|
|
CREMenu *pMenu;
|
|
|
|
switch(msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
m_hwnd = hwnd;
|
|
m_hwndEdit=GetDlgItem(hwnd, idcTxtSource);
|
|
DllAddRef();
|
|
AddRef();
|
|
|
|
if (m_pMsg &&
|
|
m_pMsg->GetMessageSource(&pstm, 0)==S_OK)
|
|
{
|
|
ZeroMemory((LPVOID)&cf, sizeof(CHARFORMAT));
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_SIZE|CFM_COLOR|CFM_FACE|CFM_BOLD|
|
|
CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
|
|
StrCpyN(cf.szFaceName, TEXT("Courier New"), ARRAYSIZE(cf.szFaceName));
|
|
cf.yHeight = 200;
|
|
cf.crTextColor = 0;
|
|
cf.dwEffects |= CFE_AUTOCOLOR;
|
|
cf.bPitchAndFamily = FIXED_PITCH;
|
|
cf.yOffset = 0;
|
|
SendMessage(m_hwndEdit, EM_SETCHARFORMAT, 0, (LPARAM)&cf);
|
|
SendMessage(m_hwndEdit, EM_SETBKGNDCOLOR, 0, (LONG)GetSysColor(COLOR_3DFACE));
|
|
SendMessage(m_hwndEdit, EM_SETLIMITTEXT, 0, 0x100000);
|
|
|
|
pMenu = new CREMenu();
|
|
if (pMenu)
|
|
{
|
|
pMenu->Init(m_hwndEdit, idmrCtxtViewSrc);
|
|
SendMessage(m_hwndEdit, EM_SETOLECALLBACK, 0, (LPARAM)pMenu);
|
|
pMenu->Release();
|
|
}
|
|
|
|
RicheditStreamIn(m_hwndEdit, pstm, SF_TEXT);
|
|
_BoldKids();
|
|
pstm->Release();
|
|
}
|
|
PostMessage(m_hwndEdit, EM_SETSEL, 0, 0);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case idmCopy:
|
|
SendMessage(m_hwndEdit, WM_COPY, 0, 0);
|
|
return TRUE;
|
|
|
|
case idmSelectAll:
|
|
Edit_SetSel(m_hwndEdit, 0, -1);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
SetWindowPos(m_hwndEdit,0,0,0,
|
|
LOWORD(lParam), HIWORD(lParam),SWP_NOACTIVATE|SWP_NOZORDER);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
DllRelease();
|
|
Release();
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
DestroyWindow(hwnd);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CViewSource::_BoldKids()
|
|
{
|
|
HBODY hBody;
|
|
FINDBODY fb={0};
|
|
CHARFORMAT cf;
|
|
BODYOFFSETS rOffset;
|
|
|
|
ZeroMemory((LPVOID)&cf, sizeof(CHARFORMAT));
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_BOLD|CFM_ITALIC;
|
|
|
|
// bold the root
|
|
m_pMsg->GetBodyOffsets(HBODY_ROOT, &rOffset);
|
|
Edit_SetSel(m_hwndEdit, 0, rOffset.cbBodyStart);
|
|
cf.dwEffects=CFE_BOLD;
|
|
SendMessage(m_hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
// bold the kids
|
|
if (m_pMsg->FindFirst(&fb, &hBody)==S_OK)
|
|
{
|
|
do
|
|
{
|
|
// italic the boundaries and bold the headers
|
|
m_pMsg->GetBodyOffsets(hBody, &rOffset);
|
|
|
|
Edit_SetSel(m_hwndEdit, rOffset.cbBoundaryStart, rOffset.cbHeaderStart);
|
|
cf.dwEffects=CFE_ITALIC;
|
|
SendMessage(m_hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
Edit_SetSel(m_hwndEdit, rOffset.cbHeaderStart, rOffset.cbBodyStart);
|
|
cf.dwEffects=CFE_BOLD;
|
|
SendMessage(m_hwndEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
|
|
|
|
}
|
|
while (m_pMsg->FindNext(&fb, &hBody)==S_OK);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
CREMenu::CREMenu()
|
|
{
|
|
m_hwndEdit = NULL;
|
|
m_cRef = 1;
|
|
}
|
|
|
|
|
|
CREMenu::~CREMenu()
|
|
{
|
|
}
|
|
|
|
|
|
ULONG CREMenu::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
ULONG CREMenu::Release()
|
|
{
|
|
if (--m_cRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
HRESULT CREMenu::QueryInterface(REFIID riid, LPVOID FAR * lplpObj)
|
|
{
|
|
*lplpObj = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
*lplpObj = (void*)(IUnknown*)this;
|
|
else if (IsEqualIID(riid, IID_IRichEditOleCallback))
|
|
*lplpObj = (void*)(IRichEditOleCallback*)this;
|
|
else
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CREMenu::Init(HWND hwndEdit, int idMenu)
|
|
{
|
|
m_hwndEdit = hwndEdit;
|
|
m_idMenu = idMenu;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CREMenu::GetNewStorage (LPSTORAGE FAR * ppstg)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::GetInPlaceContext( LPOLEINPLACEFRAME *lplpFrame, LPOLEINPLACEUIWINDOW *lplpDoc,
|
|
LPOLEINPLACEFRAMEINFO lpFrameInfo)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::ShowContainerUI(BOOL fShow)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::DeleteObject(LPOLEOBJECT lpoleobj)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::QueryAcceptData( LPDATAOBJECT pdataobj, CLIPFORMAT *pcfFormat, DWORD reco,
|
|
BOOL fReally, HGLOBAL hMetaPict)
|
|
{
|
|
*pcfFormat = CF_TEXT;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CREMenu::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::GetClipboardData(CHARRANGE *pchrg, DWORD reco, LPDATAOBJECT *ppdataobj)
|
|
{
|
|
HRESULT hr;
|
|
DATAOBJINFO *pDataInfo=NULL;
|
|
FORMATETC fetc;
|
|
TEXTRANGE txtRange;
|
|
CHARRANGE chrg;
|
|
LONG cchStart=0,
|
|
cchEnd=0,
|
|
cchMax=0,
|
|
cchLen=0;
|
|
LPSTR pszData=0;
|
|
|
|
*ppdataobj = NULL;
|
|
|
|
if (pchrg)
|
|
{
|
|
chrg = *pchrg;
|
|
|
|
cchMax = (LONG) SendMessage(m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
|
|
|
|
// validate the range
|
|
chrg.cpMin = max(0, chrg.cpMin);
|
|
chrg.cpMin = min(cchMax, chrg.cpMin);
|
|
|
|
if(chrg.cpMax < 0 || chrg.cpMax > cchMax)
|
|
chrg.cpMax = cchMax;
|
|
}
|
|
else
|
|
{
|
|
// if no charrange, then get the current selection
|
|
SendMessage(m_hwndEdit, EM_GETSEL, (WPARAM)&cchStart, (LPARAM)&cchEnd);
|
|
chrg.cpMin = cchStart;
|
|
chrg.cpMax = cchEnd;
|
|
}
|
|
|
|
if (chrg.cpMin >= chrg.cpMax)
|
|
{
|
|
*ppdataobj = NULL;
|
|
return chrg.cpMin == chrg.cpMax ? NOERROR : E_INVALIDARG;
|
|
}
|
|
|
|
|
|
cchLen = chrg.cpMax - chrg.cpMin;
|
|
|
|
if (!MemAlloc((LPVOID *)&pszData, cchLen+1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
txtRange.chrg = chrg;
|
|
txtRange.lpstrText = pszData;
|
|
|
|
SendMessage(m_hwndEdit, EM_GETTEXTRANGE, 0, (LPARAM)&txtRange);
|
|
|
|
if (!MemAlloc((LPVOID*)&pDataInfo, sizeof(DATAOBJINFO)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto error;
|
|
}
|
|
|
|
SETDefFormatEtc(pDataInfo->fe, CF_TEXT, TYMED_HGLOBAL);
|
|
|
|
pDataInfo->cbData = cchLen+1;
|
|
pDataInfo->pData = pszData;
|
|
|
|
hr = CreateDataObject(pDataInfo, 1, (PFNFREEDATAOBJ)FreeViewSrcDataObj, ppdataobj);
|
|
if (FAILED(hr))
|
|
goto error;
|
|
|
|
pDataInfo = NULL; // freed by dataobject
|
|
pszData = NULL;
|
|
|
|
error:
|
|
SafeMemFree(pszData);
|
|
SafeMemFree(pDataInfo);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CREMenu::GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CREMenu::GetContextMenu(WORD seltype, LPOLEOBJECT pOleObject, CHARRANGE *pchrg, HMENU *phMenu)
|
|
{
|
|
HMENU hMenu;
|
|
|
|
if (!(hMenu=LoadPopupMenu(m_idMenu)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (SendMessage(m_hwndEdit, EM_SELECTIONTYPE, 0, 0)==SEL_EMPTY)
|
|
{
|
|
EnableMenuItem(hMenu, idmCopy, MF_GRAYED|MF_BYCOMMAND);
|
|
EnableMenuItem(hMenu, idmCut, MF_GRAYED|MF_BYCOMMAND);
|
|
}
|
|
|
|
if (GetWindowLong(m_hwndEdit, GWL_STYLE) & ES_READONLY)
|
|
{
|
|
EnableMenuItem(hMenu, idmCut, MF_GRAYED|MF_BYCOMMAND);
|
|
EnableMenuItem(hMenu, idmPaste, MF_GRAYED|MF_BYCOMMAND);
|
|
}
|
|
|
|
*phMenu=hMenu;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ULONG CMsgSource::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
};
|
|
|
|
ULONG CMsgSource::Release()
|
|
{
|
|
m_cRef--;
|
|
if (m_cRef == 0)
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
|
|
CMsgSource::CMsgSource()
|
|
{
|
|
m_hwnd = 0;
|
|
m_cRef = 1;
|
|
m_fColor=0;
|
|
m_fDisabled=0;
|
|
m_pCmdTargetParent=0;
|
|
m_pszLastText = 0;
|
|
}
|
|
|
|
CMsgSource::~CMsgSource()
|
|
{
|
|
SafeMemFree(m_pszLastText);
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::Init(HWND hwndParent, int id, IOleCommandTarget *pCmdTargetParent)
|
|
{
|
|
CHARFORMAT cf;
|
|
CREMenu *pMenu;
|
|
|
|
DemandLoadRichEdit();
|
|
|
|
m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,
|
|
"RICHEDIT",
|
|
NULL,
|
|
WS_CHILD|WS_TABSTOP|ES_MULTILINE|ES_SAVESEL|ES_WANTRETURN|WS_VSCROLL|ES_AUTOVSCROLL,
|
|
0, 0, 0, 0,
|
|
hwndParent,
|
|
(HMENU)IntToPtr(id),
|
|
g_hLocRes,
|
|
NULL);
|
|
if (!m_hwnd)
|
|
return E_FAIL;
|
|
|
|
ZeroMemory((LPVOID)&cf, sizeof(CHARFORMAT));
|
|
cf.cbSize = sizeof(CHARFORMAT);
|
|
cf.dwMask = CFM_SIZE|CFM_COLOR|CFM_FACE|CFM_BOLD|
|
|
CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;
|
|
StrCpyN(cf.szFaceName, TEXT("Courier New"), ARRAYSIZE(cf.szFaceName));
|
|
cf.yHeight = 200;
|
|
cf.crTextColor = 0;
|
|
cf.dwEffects |= CFE_AUTOCOLOR;
|
|
cf.bPitchAndFamily = FIXED_PITCH;
|
|
cf.bCharSet = DEFAULT_CHARSET;
|
|
cf.yOffset = 0;
|
|
SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, (LPARAM)&cf);
|
|
SendMessage(m_hwnd, EM_SETEVENTMASK, 0, ENM_KEYEVENTS|ENM_CHANGE|ENM_SELCHANGE|ENM_UPDATE);
|
|
SendMessage(m_hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_SELECTIONBAR);
|
|
|
|
pMenu = new CREMenu();
|
|
if (pMenu)
|
|
{
|
|
pMenu->Init(m_hwnd, idmrCtxtViewSrc);
|
|
SendMessage(m_hwnd, EM_SETOLECALLBACK, 0, (LPARAM)pMenu);
|
|
pMenu->Release();
|
|
}
|
|
|
|
m_pCmdTargetParent = pCmdTargetParent; // loose reference, as parent never changes
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMsgSource::Show(BOOL fOn, BOOL fColor)
|
|
{
|
|
ShowWindow(m_hwnd, fOn?SW_SHOW:SW_HIDE);
|
|
m_fDisabled = !fColor;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::Load(IStream *pstm)
|
|
{
|
|
RicheditStreamIn(m_hwnd, pstm, SF_TEXT);
|
|
Edit_SetModify(m_hwnd, FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMsgSource::IsDirty()
|
|
{
|
|
return Edit_GetModify(m_hwnd) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::Save(IStream **ppstm)
|
|
{
|
|
if (MimeOleCreateVirtualStream(ppstm)!=S_OK)
|
|
return E_FAIL;
|
|
|
|
RicheditStreamOut(m_hwnd, *ppstm, SF_TEXT);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMsgSource::SetRect(RECT *prc)
|
|
{
|
|
SetWindowPos(m_hwnd, 0, prc->left, prc->top, prc->right-prc->left, prc->bottom-prc->top, SWP_NOACTIVATE|SWP_NOZORDER);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMsgSource::QueryInterface(REFIID riid, LPVOID FAR *lplpObj)
|
|
{
|
|
if(!lplpObj)
|
|
return E_INVALIDARG;
|
|
|
|
*lplpObj = NULL; // set to NULL, in case we fail.
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
*lplpObj = (LPVOID)(LPOLECOMMANDTARGET)this;
|
|
else if (IsEqualIID(riid, IID_IOleCommandTarget))
|
|
*lplpObj = (LPVOID)(LPOLECOMMANDTARGET)this;
|
|
else
|
|
return E_NOINTERFACE;
|
|
|
|
AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
|
|
{
|
|
ULONG uCmd;
|
|
|
|
if (pguidCmdGroup == NULL)
|
|
{
|
|
for (uCmd=0;uCmd<cCmds; uCmd++)
|
|
{
|
|
prgCmds[uCmd].cmdf = 0;
|
|
|
|
if (GetFocus() == m_hwnd)
|
|
{
|
|
switch (prgCmds[uCmd].cmdID)
|
|
{
|
|
case OLECMDID_CUT:
|
|
case OLECMDID_COPY:
|
|
if (SendMessage(m_hwnd, EM_SELECTIONTYPE, 0, 0)!=SEL_EMPTY)
|
|
prgCmds[uCmd].cmdf = MSOCMDF_ENABLED;
|
|
break;
|
|
|
|
case OLECMDID_SELECTALL:
|
|
prgCmds[uCmd].cmdf = MSOCMDF_ENABLED;
|
|
break;
|
|
|
|
case OLECMDID_UNDO:
|
|
if (SendMessage(m_hwnd, EM_CANUNDO, 0, 0))
|
|
prgCmds[uCmd].cmdf = MSOCMDF_ENABLED;
|
|
break;
|
|
|
|
case OLECMDID_PASTE:
|
|
if (SendMessage(m_hwnd, EM_CANPASTE, 0, 0))
|
|
prgCmds[uCmd].cmdf = MSOCMDF_ENABLED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID(*pguidCmdGroup, CMDSETID_MimeEdit))
|
|
{
|
|
// disable all these commands
|
|
for (uCmd=0; uCmd < cCmds; uCmd++)
|
|
{
|
|
// bail if we see MECMDID_SHOWSOURCETABS and goto default handler
|
|
if (prgCmds[uCmd].cmdID == MECMDID_SHOWSOURCETABS)
|
|
return OLECMDERR_E_UNKNOWNGROUP;
|
|
|
|
prgCmds[uCmd].cmdf = 0;
|
|
}
|
|
return S_OK;
|
|
}
|
|
else if (IsEqualGUID(*pguidCmdGroup, CMDSETID_Forms3))
|
|
{
|
|
// disable all these commands
|
|
for (uCmd=0; uCmd < cCmds; uCmd++)
|
|
{
|
|
prgCmds[uCmd].cmdf = 0;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
return OLECMDERR_E_UNKNOWNGROUP;
|
|
}
|
|
|
|
HRESULT CMsgSource::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
|
|
{
|
|
if (pguidCmdGroup == NULL)
|
|
{
|
|
switch (nCmdID)
|
|
{
|
|
case OLECMDID_CUT:
|
|
SendMessage(m_hwnd, WM_CUT, 0, 0);
|
|
return S_OK;
|
|
|
|
case OLECMDID_COPY:
|
|
SendMessage(m_hwnd, WM_COPY, 0, 0);
|
|
return S_OK;
|
|
|
|
case OLECMDID_PASTE:
|
|
SendMessage(m_hwnd, WM_PASTE, 0, 0);
|
|
return S_OK;
|
|
|
|
case OLECMDID_SELECTALL:
|
|
SendMessage(m_hwnd, EM_SETSEL, 0, -1);
|
|
return S_OK;
|
|
|
|
case OLECMDID_UNDO:
|
|
SendMessage(m_hwnd, EM_UNDO, 0, 0);
|
|
return S_OK;
|
|
|
|
default:
|
|
return OLECMDERR_E_NOTSUPPORTED;
|
|
}
|
|
}
|
|
return OLECMDERR_E_UNKNOWNGROUP;
|
|
}
|
|
|
|
HRESULT CMsgSource::OnWMNotify(WPARAM wParam, NMHDR* pnmhdr, LRESULT *plRet)
|
|
{
|
|
MSGFILTER *pmf;
|
|
|
|
*plRet = 0;
|
|
|
|
if (pnmhdr->hwndFrom != m_hwnd)
|
|
return S_FALSE;
|
|
|
|
switch (pnmhdr->code)
|
|
{
|
|
case EN_MSGFILTER:
|
|
{
|
|
// if we get a control-tab, then richedit snags this and inserts a
|
|
// tab char, we hook the wm_keydown and never pass to richedit
|
|
if (((MSGFILTER *)pnmhdr)->msg == WM_KEYDOWN &&
|
|
((MSGFILTER *)pnmhdr)->wParam == VK_TAB &&
|
|
(GetKeyState(VK_CONTROL) & 0x8000))
|
|
{
|
|
*plRet = TRUE;
|
|
return S_OK;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EN_SELCHANGE:
|
|
{
|
|
if (m_pCmdTargetParent)
|
|
m_pCmdTargetParent->Exec(NULL, OLECMDID_UPDATECOMMANDS, NULL, NULL, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
static HACCEL g_hAccelSrc=0;
|
|
|
|
HRESULT CMsgSource::TranslateAccelerator(LPMSG lpmsg)
|
|
{
|
|
MSG msg;
|
|
|
|
if (GetFocus() != m_hwnd)
|
|
return S_FALSE;
|
|
|
|
if (!g_hAccelSrc) // cache this as NT4 SP3 leaks internal accerator tables
|
|
g_hAccelSrc = LoadAccelerators(g_hLocRes, MAKEINTRESOURCE(idacSrcView));
|
|
|
|
// see if it one of ours
|
|
if (::TranslateAcceleratorWrapW(GetParent(m_hwnd), g_hAccelSrc, &msg))
|
|
return S_OK;
|
|
|
|
|
|
// insert tabs
|
|
if (lpmsg->message == WM_KEYDOWN &&
|
|
lpmsg->wParam == VK_TAB &&
|
|
!(GetKeyState(VK_CONTROL) & 0x8000) &&
|
|
!(GetKeyState(VK_SHIFT) & 0x8000))
|
|
{
|
|
Edit_ReplaceSel(m_hwnd, TEXT("\t"));
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::OnWMCommand(HWND hwnd, int id, WORD wCmd)
|
|
{
|
|
if (GetFocus() == m_hwnd)
|
|
{
|
|
// context menu commands
|
|
switch (id)
|
|
{
|
|
case idmTab:
|
|
Edit_ReplaceSel(m_hwnd, TEXT("\t"));
|
|
return S_OK;
|
|
|
|
case idmCopy:
|
|
SendMessage(m_hwnd, WM_COPY, 0, 0);
|
|
return S_OK;
|
|
|
|
case idmPaste:
|
|
SendMessage(m_hwnd, WM_PASTE, 0, 0);
|
|
return S_OK;
|
|
|
|
case idmCut:
|
|
SendMessage(m_hwnd, WM_CUT, 0, 0);
|
|
return S_OK;
|
|
|
|
case idmUndo:
|
|
SendMessage(m_hwnd, EM_UNDO, 0, 0);
|
|
return S_OK;
|
|
|
|
case idmSelectAll:
|
|
SendMessage(m_hwnd, EM_SETSEL, 0, -1);
|
|
return S_OK;
|
|
|
|
}
|
|
}
|
|
|
|
if (hwnd != m_hwnd) // not our window
|
|
return S_FALSE;
|
|
|
|
if (wCmd == EN_CHANGE)
|
|
{
|
|
OnChange();
|
|
return S_OK;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
HRESULT CMsgSource::HasFocus()
|
|
{
|
|
return GetFocus() == m_hwnd ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT CMsgSource::SetFocus()
|
|
{
|
|
::SetFocus(m_hwnd);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void CMsgSource::OnChange()
|
|
{
|
|
// batch up the change commands with a timer
|
|
if (!m_fColor)
|
|
{
|
|
KillTimer(GetParent(m_hwnd), idTimerEditChange);
|
|
SetTimer(GetParent(m_hwnd), idTimerEditChange, 200, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::OnTimer(WPARAM idTimer)
|
|
{
|
|
CHARFORMAT cf;
|
|
int inTag = 0;
|
|
BOOL pastTag = FALSE;
|
|
COLORREF crTag = RGB(0x80, 0, 0x80);
|
|
COLORREF crInTag = RGB(0xFF, 0, 0);
|
|
COLORREF crNormal = RGB(0, 0, 0);
|
|
COLORREF crLiteral = RGB(0, 0, 0xFF);
|
|
COLORREF crHere;
|
|
COLORREF crLast = crNormal;
|
|
int i, n;
|
|
int nChange=0;
|
|
BOOL bHidden = FALSE;
|
|
CHARRANGE cr;
|
|
int ignoreTags = ENV_NORMAL;
|
|
char quote_1 = QUOTE_1;
|
|
char quote_2 = QUOTE_2;
|
|
BOOL fRestoreScroll=FALSE,
|
|
fShowProgress=FALSE;
|
|
DWORD dwStartTime = GetTickCount();
|
|
HCURSOR hCur = NULL;
|
|
DWORD dwProgress=0,
|
|
dwTmp;
|
|
VARIANTARG va;
|
|
TCHAR rgch[CCHMAX_STRINGRES],
|
|
rgchFmt[CCHMAX_STRINGRES];
|
|
LPSTR pszText=0;
|
|
int cch;
|
|
BOOL fSetTimer=FALSE;
|
|
|
|
// Save modificationness
|
|
BOOL bModified = Edit_GetModify(m_hwnd);
|
|
|
|
if (idTimer!=idTimerEditChange)
|
|
return S_FALSE;
|
|
|
|
// Kill outstanding timer
|
|
KillTimer(GetParent(m_hwnd), idTimerEditChange);
|
|
|
|
//
|
|
// If the user is mousing around (say for scrolling) then don't drag down
|
|
// his performance!
|
|
//
|
|
if (GetCapture())
|
|
{
|
|
SetTimer(GetParent(m_hwnd), idTimerEditChange, 200, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
// Turn off the color syntax
|
|
if (m_fDisabled)
|
|
{
|
|
// Already all one color
|
|
|
|
m_fColor = TRUE;
|
|
|
|
// Save current selection and hide
|
|
GetSel(&cr);
|
|
HideSelection(TRUE, FALSE);
|
|
bHidden = TRUE;
|
|
|
|
SetSel(0, -1); // select all
|
|
GetSelectionCharFormat(&cf);
|
|
cf.dwMask = CFM_COLOR;
|
|
cf.dwEffects = 0;
|
|
cf.crTextColor = crNormal;
|
|
SetSelectionCharFormat(&cf);
|
|
}
|
|
else
|
|
{
|
|
// Start color fiddling
|
|
// Get text, find the change
|
|
|
|
if (_GetText(&pszText)!=S_OK)
|
|
return E_FAIL;
|
|
|
|
const char* start = (const char*)pszText;
|
|
const char* old = (const char*) m_pszLastText;
|
|
const char* s;
|
|
|
|
for (s = start; *s && old && *old && *s == *old; s++, old++)
|
|
continue;
|
|
|
|
// If no change, nothing to do
|
|
if (*s == 0)
|
|
{
|
|
MemFree(pszText);
|
|
return S_OK;
|
|
}
|
|
|
|
// Otherwise, track place where we'll start to examine colors for changes
|
|
nChange = (int) (s - start);
|
|
|
|
// Only examine 2000 chars at a time
|
|
if (lstrlen(s) > 2000)
|
|
{
|
|
// Reset timer to process other characters
|
|
fSetTimer = TRUE;
|
|
|
|
// Truncate text so we only examine limited amount
|
|
cch = lstrlen(pszText);
|
|
cch = min(cch, nChange + 2000);
|
|
pszText[cch] = 0;
|
|
start = (const char*)pszText;
|
|
}
|
|
|
|
SafeMemFree(m_pszLastText);
|
|
m_pszLastText = pszText;
|
|
|
|
m_fColor = TRUE;
|
|
|
|
// Workaround for scrolling bug in REC 1.0
|
|
if (GetFocus() == m_hwnd)
|
|
{
|
|
SendMessage(m_hwnd, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
|
|
// BUGBUG: richedit1.0 on NT4 will remove the WS_VISIBLE bit when XORing ECO_AUTOVSCROLL
|
|
// call show window after this to esure that the visible bit is displayed
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
fRestoreScroll=TRUE;
|
|
}
|
|
|
|
const char* range = start;
|
|
for (s = start; *s; s++)
|
|
{
|
|
// if we've been going for >2 seconds then show an hourglass and
|
|
// start showing progress
|
|
if (hCur == NULL &&
|
|
GetTickCount() >= dwStartTime + 2000)
|
|
{
|
|
hCur = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
if (m_pCmdTargetParent)
|
|
{
|
|
fShowProgress=TRUE;
|
|
*rgchFmt=NULL;
|
|
LoadString(g_hLocRes, idsColorSourcePC, rgchFmt, ARRAYSIZE(rgchFmt));
|
|
}
|
|
}
|
|
|
|
if (fShowProgress)
|
|
{
|
|
dwTmp = (DWORD) ((((s - start))*100)/cch);
|
|
if (dwTmp > dwProgress)
|
|
{
|
|
// did overall percentage change, if so update statusbar
|
|
dwProgress = dwTmp;
|
|
|
|
wnsprintf(rgch, ARRAYSIZE(rgch), rgchFmt, dwProgress);
|
|
va.vt = VT_BSTR;
|
|
if (HrLPSZToBSTR(rgch, &va.bstrVal)==S_OK)
|
|
{
|
|
m_pCmdTargetParent->Exec(NULL, OLECMDID_SETPROGRESSTEXT, 0, &va, NULL);
|
|
SysFreeString(va.bstrVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
// entering/leaving string literal in tag
|
|
if (inTag && (*s == quote_1 || *s == quote_2))
|
|
{
|
|
if (ignoreTags == ENV_QUOTE ||
|
|
ignoreTags == ENV_QUOTE_SCR) //leaving
|
|
{
|
|
ignoreTags = (ignoreTags == ENV_QUOTE) ?
|
|
ENV_NORMAL : ENV_SCRIPT;
|
|
quote_1 = QUOTE_1;
|
|
quote_2 = QUOTE_2;
|
|
}
|
|
else if (ignoreTags == ENV_NORMAL) // entering
|
|
{
|
|
ignoreTags = ENV_QUOTE;
|
|
quote_1 = quote_2 = *s;
|
|
}
|
|
else if (ignoreTags == ENV_SCRIPT) // entering
|
|
{
|
|
ignoreTags = ENV_QUOTE_SCR;
|
|
quote_1 = quote_2 = *s;
|
|
}
|
|
}
|
|
// Update leaving tag
|
|
else if (*s == '>')
|
|
{
|
|
switch (ignoreTags) // end of env?
|
|
{
|
|
case ENV_DENALI:
|
|
if (s-1>=start && *(s-1) == '%')
|
|
ignoreTags = ENV_NORMAL;
|
|
break;
|
|
|
|
case ENV_COMMENT:
|
|
if (s-2>=start && *(s-1) == '-' && *(s-2) == '-')
|
|
ignoreTags = ENV_NORMAL;
|
|
break;
|
|
|
|
case ENV_SCRIPT:
|
|
if (s-7>=start &&
|
|
StrCmpNIA(s-7, "/SCRIPT", 7)==0)
|
|
{
|
|
ignoreTags = ENV_NORMAL;
|
|
// Color </SCRIPT> properly
|
|
pastTag = TRUE;
|
|
inTag = 0;
|
|
s-=8;
|
|
}
|
|
else // end of <script ...>
|
|
inTag = 0;
|
|
break;
|
|
|
|
default: // <SCRIPT> (no attribs)
|
|
if (inTag && s-7>=start &&
|
|
StrCmpNIA(s-7, "<SCRIPT", 7)==0)
|
|
{
|
|
ignoreTags = ENV_SCRIPT;
|
|
pastTag = TRUE;
|
|
inTag = 0;
|
|
}
|
|
}
|
|
if (ignoreTags == ENV_NORMAL)
|
|
{
|
|
pastTag = TRUE;
|
|
inTag--;
|
|
if (inTag < 0)
|
|
inTag = 0;
|
|
}
|
|
}
|
|
|
|
// Check color
|
|
crHere = inTag ?
|
|
(pastTag ?
|
|
( (*s != '\"' &&
|
|
(ignoreTags == ENV_QUOTE || ignoreTags == ENV_QUOTE_SCR)
|
|
) ?
|
|
crLiteral : crInTag )
|
|
: crTag)
|
|
: crNormal;
|
|
|
|
// If different from last, need to update previous range
|
|
if (crHere != crLast)
|
|
{
|
|
i = (int) (range - start);
|
|
n = (int)(s - range);
|
|
|
|
if (i+n >= nChange)
|
|
{
|
|
if (!bHidden)
|
|
{
|
|
// Save current selection and hide
|
|
GetSel(&cr);
|
|
HideSelection(TRUE, FALSE);
|
|
bHidden = TRUE;
|
|
}
|
|
|
|
SetSel(i, i+n);
|
|
GetSelectionCharFormat(&cf);
|
|
// If color over range varies or doesn't match, need to apply color
|
|
if ((cf.dwMask & CFM_COLOR) == 0 || cf.crTextColor != crLast)
|
|
{
|
|
cf.dwMask = CFM_COLOR;
|
|
cf.dwEffects = 0;
|
|
cf.crTextColor = crLast;
|
|
SetSelectionCharFormat(&cf);
|
|
}
|
|
}
|
|
|
|
// Reset range
|
|
range = s;
|
|
crLast = crHere;
|
|
}
|
|
|
|
// Now update entering tag
|
|
if (*s == '<' && ignoreTags == ENV_NORMAL)
|
|
{
|
|
inTag++;
|
|
if (inTag == 1)
|
|
pastTag = FALSE;
|
|
}
|
|
else if (inTag && !pastTag && isspace(*s))
|
|
{
|
|
pastTag = TRUE;
|
|
if (s-1 >= start && *(s-1) == '%')
|
|
ignoreTags = ENV_DENALI;
|
|
else if (s-3 >= start && *(s-1) == '-'
|
|
&& *(s-2) == '-' && *(s-3) == '!')
|
|
ignoreTags = ENV_COMMENT;
|
|
else if (inTag && s-7>=start &&
|
|
StrCmpNIA(s-7, "<SCRIPT", 7)==0)
|
|
ignoreTags = ENV_SCRIPT;
|
|
}
|
|
}
|
|
|
|
// Make sure last range is right
|
|
i = (int) (range - start);
|
|
n = (int) (s - range);
|
|
|
|
if (i+n >= nChange)
|
|
{
|
|
if (!bHidden)
|
|
{
|
|
// Save current selection and hide
|
|
GetSel(&cr);
|
|
HideSelection(TRUE, FALSE);
|
|
bHidden = TRUE;
|
|
}
|
|
SetSel(i, i+n);
|
|
GetSelectionCharFormat(&cf);
|
|
// If color over range varies or doesn't match, need to apply color
|
|
if ((cf.dwMask & CFM_COLOR) == 0 || cf.crTextColor != crLast)
|
|
{
|
|
cf.dwMask = CFM_COLOR;
|
|
cf.dwEffects = 0;
|
|
cf.crTextColor = crLast;
|
|
SetSelectionCharFormat(&cf);
|
|
}
|
|
}
|
|
// Workaround for scrolling bug in REC 1.0
|
|
if (fRestoreScroll)
|
|
{
|
|
// BUGBUG: richedit1.0 on NT4 will remove the WS_VISIBLE bit when ORing ECO_AUTOVSCROLL
|
|
// call show window after this to esure that the visible bit is displayed
|
|
SendMessage(m_hwnd, EM_SETOPTIONS, ECOOP_OR, ECO_AUTOVSCROLL);
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
}
|
|
} // End color fiddling
|
|
|
|
// Restore selection visibility
|
|
if (bHidden)
|
|
{
|
|
SetSel(cr.cpMin, cr.cpMax);
|
|
HideSelection(FALSE, FALSE);
|
|
}
|
|
|
|
// Restore modificationness
|
|
if (!bModified)
|
|
Edit_SetModify(m_hwnd, bModified);
|
|
|
|
if (fShowProgress)
|
|
{
|
|
va.vt = VT_BSTR;
|
|
va.bstrVal=NULL;
|
|
m_pCmdTargetParent->Exec(NULL, OLECMDID_SETPROGRESSTEXT, 0, &va, NULL);
|
|
}
|
|
|
|
if (hCur)
|
|
SetCursor(hCur);
|
|
|
|
m_fColor = FALSE;
|
|
if (fSetTimer)
|
|
SetTimer(GetParent(m_hwnd), idTimerEditChange, 200, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
void CMsgSource::HideSelection(BOOL fHide, BOOL fChangeStyle)
|
|
{
|
|
SendMessage(m_hwnd, EM_HIDESELECTION, fHide, fChangeStyle);
|
|
}
|
|
|
|
void CMsgSource::GetSel(CHARRANGE *pcr)
|
|
{
|
|
SendMessage(m_hwnd, EM_EXGETSEL, 0, (LPARAM)pcr);
|
|
}
|
|
|
|
void CMsgSource::SetSel(int nStart, int nEnd)
|
|
{
|
|
SendMessage(m_hwnd, EM_SETSEL, nStart, nEnd);
|
|
}
|
|
|
|
extern BOOL g_fCanEditBiDi;
|
|
void CMsgSource::GetSelectionCharFormat(CHARFORMAT *pcf)
|
|
{
|
|
pcf->cbSize = sizeof(CHARFORMAT);
|
|
pcf->dwMask = CFM_BOLD|CFM_COLOR|CFM_FACE|CFM_ITALIC|CFM_OFFSET|CFM_PROTECTED|CFM_SIZE|CFM_STRIKEOUT|CFM_UNDERLINE;
|
|
|
|
SendMessage(m_hwnd, EM_GETCHARFORMAT, TRUE, (LPARAM)pcf);
|
|
|
|
// On BiDi win9x, DEFAULT_CHARSET is treated as ANSI !!
|
|
// need to reassign charset (Arabic for Arabic and Hebrew for Hebrew)
|
|
if(g_fCanEditBiDi && (!pcf->bCharSet || pcf->bCharSet == DEFAULT_CHARSET))
|
|
{
|
|
// The best way to determine the OS language is from system font charset
|
|
LOGFONT lfSystem;
|
|
static BYTE lfCharSet = 0 ; // RunOnce
|
|
if(!lfCharSet && GetObject(GetStockObject(SYSTEM_FONT), sizeof(lfSystem), (LPVOID)& lfSystem))
|
|
{
|
|
if (lfSystem.lfCharSet == ARABIC_CHARSET
|
|
|| lfSystem.lfCharSet == HEBREW_CHARSET)
|
|
{
|
|
lfCharSet = lfSystem.lfCharSet; // Arabic/Hebrew charset for Arabic/Hebrew OS
|
|
}
|
|
}
|
|
pcf->bCharSet = lfCharSet;
|
|
}
|
|
}
|
|
|
|
|
|
void CMsgSource::SetSelectionCharFormat(CHARFORMAT *pcf)
|
|
{
|
|
pcf->cbSize = sizeof(CHARFORMAT);
|
|
|
|
SendMessage(m_hwnd, EM_SETCHARFORMAT, TRUE, (LPARAM)pcf);
|
|
}
|
|
|
|
|
|
HRESULT CMsgSource::SetDirty(BOOL fDirty)
|
|
{
|
|
Edit_SetModify(m_hwnd, fDirty);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CMsgSource::_GetText(LPSTR *ppsz)
|
|
{
|
|
LPSTR psz;
|
|
int cch;
|
|
|
|
*ppsz = 0;
|
|
|
|
cch = GetWindowTextLength(m_hwnd);
|
|
|
|
if (!MemAlloc((LPVOID *)&psz, sizeof(TCHAR) * cch+1))
|
|
return E_OUTOFMEMORY;
|
|
|
|
*psz = 0;
|
|
GetWindowText(m_hwnd, psz, cch);
|
|
*ppsz = psz;
|
|
return S_OK;
|
|
}
|