1193 lines
32 KiB
C++
1193 lines
32 KiB
C++
//-------------------------------------------------------------------------//
|
|
// link.cpp - implementation of CLink
|
|
//
|
|
// [scotthan] - created 10/7/98
|
|
// [markfi] - ported to UxCtrl 3/00
|
|
// [t-jklann] - uses markup 7/00
|
|
|
|
// issues: removed window capture functionality; shouldn't change much
|
|
|
|
#include <ctlspriv.h>
|
|
#include <markup.h>
|
|
#include <oleacc.h>
|
|
|
|
#define DllAddRef()
|
|
#define DllRelease()
|
|
|
|
typedef WCHAR TUCHAR, *PTUCHAR;
|
|
|
|
#define LINKCOLOR_BKGND COLOR_WINDOW
|
|
|
|
void _InitializeUISTATE(IN HWND hwnd, IN OUT UINT* puFlags);
|
|
BOOL _HandleWM_UPDATEUISTATE(IN WPARAM wParam, IN LPARAM lParam, IN OUT UINT* puFlags);
|
|
|
|
inline void MakePoint(LPARAM lParam, OUT LPPOINT ppt)
|
|
{
|
|
POINTS pts = MAKEPOINTS(lParam);
|
|
ppt->x = pts.x;
|
|
ppt->y = pts.y;
|
|
}
|
|
|
|
STDAPI_(BOOL) IsWM_GETOBJECT(UINT uMsg)
|
|
{
|
|
return WM_GETOBJECT == uMsg;
|
|
}
|
|
|
|
// common IAccessible implementation.
|
|
class CAccessibleBase : public IAccessible, public IOleWindow
|
|
{
|
|
public:
|
|
CAccessibleBase(const HWND& hwnd)
|
|
: _cRef(1), _ptiAcc(NULL), _hwnd(hwnd)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
virtual ~CAccessibleBase()
|
|
{
|
|
ATOMICRELEASE(_ptiAcc);
|
|
}
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
|
|
// IOleWindow
|
|
STDMETHODIMP GetWindow(HWND* phwnd);
|
|
STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; }
|
|
|
|
// IDispatch
|
|
STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
|
|
STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo);
|
|
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames,
|
|
LCID lcid, DISPID * rgdispid);
|
|
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
|
|
DISPPARAMS * pdispparams, VARIANT * pvarResult,
|
|
EXCEPINFO * pexcepinfo, UINT * puArgErr);
|
|
// IAccessible
|
|
STDMETHODIMP get_accParent(IDispatch ** ppdispParent);
|
|
STDMETHODIMP get_accChildCount(long * pcChildren);
|
|
STDMETHODIMP get_accChild(VARIANT varChildIndex, IDispatch ** ppdispChild);
|
|
STDMETHODIMP get_accValue(VARIANT varChild, BSTR* pbstrValue);
|
|
STDMETHODIMP get_accDescription(VARIANT varChild, BSTR * pbstrDescription);
|
|
STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole);
|
|
STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState);
|
|
STDMETHODIMP get_accHelp(VARIANT varChild, BSTR* pbstrHelp);
|
|
STDMETHODIMP get_accHelpTopic(BSTR* pbstrHelpFile, VARIANT varChild, long* pidTopic);
|
|
STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR* pbstrKeyboardShortcut);
|
|
STDMETHODIMP get_accFocus(VARIANT * pvarFocusChild);
|
|
STDMETHODIMP get_accSelection(VARIANT * pvarSelectedChildren);
|
|
STDMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR* pbstrDefaultAction);
|
|
STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild);
|
|
STDMETHODIMP accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild);
|
|
STDMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt);
|
|
STDMETHODIMP accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint);
|
|
STDMETHODIMP put_accName(VARIANT varChild, BSTR bstrName);
|
|
STDMETHODIMP put_accValue(VARIANT varChild, BSTR bstrValue);
|
|
|
|
protected:
|
|
virtual UINT GetDefaultActionStringID() const = 0;
|
|
|
|
private:
|
|
ULONG _cRef;
|
|
ITypeInfo* _ptiAcc;
|
|
const HWND& _hwnd;
|
|
|
|
// Thunked OLEACC defs from winuser.h
|
|
#ifndef OBJID_WINDOW
|
|
#define OBJID_WINDOW 0x00000000
|
|
#endif//OBJID_WINDOW
|
|
|
|
#ifndef OBJID_TITLEBAR
|
|
#define OBJID_TITLEBAR 0xFFFFFFFE
|
|
#endif//OBJID_TITLEBAR
|
|
|
|
#ifndef OBJID_CLIENT
|
|
#define OBJID_CLIENT 0xFFFFFFFC
|
|
#endif//OBJID_CLIENT
|
|
|
|
#ifndef CHILDID_SELF
|
|
#define CHILDID_SELF 0
|
|
#endif//CHILDID_SELF
|
|
|
|
#define VALIDATEACCCHILD(varChild, idChild, hrFail) \
|
|
if (!(VT_I4 == varChild.vt && idChild == varChild.lVal)) {return hrFail;}
|
|
|
|
} ;
|
|
|
|
#define TEST_CAPTURE(fTest) ((_fCapture & fTest) != 0)
|
|
#define MODIFY_CAPTURE(fSet, fRemove) {if (fSet){_fCapture |= fSet;} if (fRemove){_fCapture &= ~fRemove;}}
|
|
#define RESET_CAPTURE() {_fCapture=0;}
|
|
|
|
class CLink : public CAccessibleBase, public IMarkupCallback
|
|
{
|
|
public:
|
|
CLink();
|
|
virtual ~CLink();
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
|
|
// IMarkupCallback
|
|
STDMETHODIMP GetState(UINT uState);
|
|
STDMETHODIMP Notify(int nCode, int iLink);
|
|
STDMETHODIMP InvalidateRect(RECT* prc);
|
|
STDMETHODIMP OnCustomDraw(DWORD dwDrawStage, HDC hdc, const RECT *prc, DWORD dwItemSpec, UINT uItemState, LRESULT *pdwResult);
|
|
|
|
// IAccessible specialization
|
|
STDMETHODIMP get_accName(VARIANT varChild, BSTR* pbstrName);
|
|
STDMETHODIMP accDoDefaultAction(VARIANT varChild);
|
|
|
|
private:
|
|
// CAccessibleBase overrides
|
|
UINT GetDefaultActionStringID() const { return IDS_LINKWINDOW_DEFAULTACTION; }
|
|
|
|
// Utility methods
|
|
void Paint(HDC hdc, IN OPTIONAL LPCRECT prcClient = NULL, LPCRECT prcClip = NULL);
|
|
|
|
// Message handlers
|
|
static LRESULT WINAPI WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
LRESULT SendNotify(UINT nCode, int iLink, BOOL fGetLinkText) const;
|
|
LRESULT GetItem(OUT LITEM* pItem);
|
|
LRESULT SetItem(IN LITEM* pItem);
|
|
|
|
void UpdateTabstop();
|
|
|
|
// Data
|
|
HFONT _hfStatic;
|
|
HFONT _hfUnderline;
|
|
HWND _hwnd;
|
|
UINT _fKeyboardCues;
|
|
BOOL _bTransparent;
|
|
BOOL _bIgnoreReturn;
|
|
BOOL _fEatTabChar;
|
|
BOOL _fTabStop;
|
|
IControlMarkup* _pMarkup;
|
|
UINT _cRef;
|
|
HRESULT Initialize();
|
|
friend BOOL InitLinkClass(HINSTANCE);
|
|
friend BOOL UnInitLinkClass(HINSTANCE);
|
|
};
|
|
|
|
BOOL WINAPI InitLinkClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASSEX wc = {0};
|
|
|
|
wc.cbSize = sizeof(wc);
|
|
wc.style = CS_GLOBALCLASS;
|
|
wc.lpfnWndProc = CLink::WndProc;
|
|
wc.hInstance = hInstance;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH)(LINKCOLOR_BKGND+1);
|
|
wc.lpszClassName = WC_LINK;
|
|
|
|
if (!RegisterClassEx(&wc) && !GetClassInfoEx(hInstance, WC_LINK, &wc))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI UnInitLinkClass(HINSTANCE)
|
|
{
|
|
return ::UnregisterClass(WC_LINK, HINST_THISDLL);
|
|
}
|
|
|
|
CLink::CLink()
|
|
: CAccessibleBase(_hwnd),
|
|
_hwnd(NULL),
|
|
_fKeyboardCues(0),
|
|
_pMarkup(NULL),
|
|
_cRef(1)
|
|
{
|
|
}
|
|
|
|
CLink::~CLink()
|
|
{
|
|
if (_pMarkup)
|
|
{
|
|
_pMarkup->Release();
|
|
_pMarkup = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT CLink::Initialize()
|
|
{
|
|
// NOTE - this is the same code the old linkwindow had to find its parent's font
|
|
// I this is bogus - WM_GETFONT is spec'ed as being sent from parent to control, not
|
|
// child control to parent... We should probably find a better way of doing this.
|
|
_hfStatic = NULL;
|
|
_hfUnderline = NULL;
|
|
for (HWND hwnd = _hwnd; NULL == _hfStatic && hwnd != NULL; hwnd = GetParent(hwnd))
|
|
_hfStatic = (HFONT)::SendMessage( hwnd, WM_GETFONT, 0, 0L );
|
|
|
|
if (_hfStatic)
|
|
{
|
|
_hfUnderline = CCCreateUnderlineFont(_hfStatic);
|
|
}
|
|
|
|
// ... get a markup
|
|
return Markup_Create(SAFECAST(this, IMarkupCallback*), _hfStatic, _hfUnderline, IID_PPV_ARG(IControlMarkup, &_pMarkup));
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// CLink IUnknown implementation override (from CAccessibleBase)
|
|
//-------------------------------------------------------------------------//
|
|
|
|
// override QueryInterface from CAccessibleBase!
|
|
STDMETHODIMP CLink::QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CAccessibleBase, IDispatch),
|
|
QITABENT(CAccessibleBase, IAccessible),
|
|
QITABENT(CAccessibleBase, IOleWindow),
|
|
QITABENT(CLink, IMarkupCallback),
|
|
{ 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CLink::AddRef()
|
|
{
|
|
return InterlockedIncrement((LONG*)&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CLink::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement((LONG*)&_cRef);
|
|
if (cRef <= 0)
|
|
{
|
|
DllRelease();
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// CLink IMarkupCallback implementation
|
|
//-------------------------------------------------------------------------//
|
|
|
|
STDMETHODIMP CLink::GetState(UINT uState)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
switch(uState)
|
|
{
|
|
case MARKUPSTATE_FOCUSED:
|
|
hr = (GetFocus()==_hwnd) ? S_OK : S_FALSE;
|
|
break;
|
|
|
|
case MARKUPSTATE_ALLOWMARKUP:
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CLink::OnCustomDraw(DWORD dwDrawStage, HDC hdc, const RECT *prc, DWORD dwItemSpec, UINT uItemState, LRESULT *pdwResult)
|
|
{
|
|
NMCUSTOMDRAW nmcd;
|
|
ZeroMemory(&nmcd, sizeof(nmcd) );
|
|
|
|
nmcd.hdr.hwndFrom = _hwnd;
|
|
nmcd.hdr.idFrom = (UINT_PTR)GetWindowLong( _hwnd, GWL_ID );
|
|
nmcd.hdr.code = NM_CUSTOMDRAW;
|
|
nmcd.dwDrawStage = dwDrawStage;
|
|
nmcd.hdc = hdc;
|
|
if (prc)
|
|
CopyRect(&nmcd.rc, prc);
|
|
nmcd.dwItemSpec = dwItemSpec;
|
|
nmcd.uItemState = uItemState;
|
|
|
|
LRESULT dwRes = SendMessage(GetParent(_hwnd), WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
|
|
if (pdwResult)
|
|
*pdwResult = dwRes;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CLink::Notify(int nCode, int iLink)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
if (_pMarkup)
|
|
{
|
|
switch (nCode)
|
|
{
|
|
case MARKUPMESSAGE_WANTFOCUS:
|
|
// Markup wants focus
|
|
SetFocus(_hwnd);
|
|
break;
|
|
|
|
case MARKUPMESSAGE_KEYEXECUTE:
|
|
SendNotify(NM_RETURN, iLink, TRUE);
|
|
break;
|
|
|
|
case MARKUPMESSAGE_CLICKEXECUTE:
|
|
SendNotify(NM_CLICK, iLink, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CLink::InvalidateRect(RECT* prc)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (! ::InvalidateRect(_hwnd, prc, TRUE))
|
|
hr=S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// CLink IAccessible impl
|
|
//
|
|
// Note: Currently, this IAccessible implementation does not supports only
|
|
// single links; multiple links are not supported. All child delegation
|
|
// is to/from self. This allows us to blow off the IEnumVARIANT and IDispatch
|
|
// implementations.
|
|
//
|
|
// To shore this up the implementation, we need to implement each link
|
|
// as a child IAccessible object and delegate accordingly.
|
|
//
|
|
STDMETHODIMP CLink::get_accName(VARIANT varChild, BSTR* pbstrName)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
|
|
if (NULL == pbstrName)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (NULL == _pMarkup)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*pbstrName = NULL;
|
|
DWORD dwCch;
|
|
HRESULT hr;
|
|
if (S_OK == (hr = _pMarkup->GetText(FALSE, NULL, &dwCch)))
|
|
{
|
|
*pbstrName = SysAllocStringLen(NULL, dwCch);
|
|
if (*pbstrName)
|
|
hr = _pMarkup->GetText(FALSE, *pbstrName, &dwCch);
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CLink::accDoDefaultAction(VARIANT varChild)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
SendNotify(NM_RETURN, NULL, FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
// CLink window implementation
|
|
|
|
void CLink::Paint(HDC hdcClient, LPCRECT prcClient, LPCRECT prcClip)
|
|
{
|
|
if (!_pMarkup)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RECT rcClient;
|
|
if (!prcClient)
|
|
{
|
|
GetClientRect(_hwnd, &rcClient);
|
|
prcClient = &rcClient;
|
|
}
|
|
|
|
if (RECTWIDTH(*prcClient) <= 0 ||
|
|
RECTHEIGHT(*prcClient) <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HDC hdc = hdcClient ? hdcClient : GetDC(_hwnd);
|
|
RECT rcDraw = *prcClient; // initialize line rect
|
|
|
|
HBRUSH hbrOld = NULL;
|
|
|
|
// initialize background
|
|
HBRUSH hbr = (HBRUSH)SendMessage(GetParent(_hwnd), WM_CTLCOLORSTATIC,
|
|
(WPARAM)hdc, (LPARAM)_hwnd);
|
|
if (hbr)
|
|
hbrOld = (HBRUSH)SelectObject(hdc, hbr);
|
|
|
|
if (_bTransparent)
|
|
{
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
}
|
|
else
|
|
{
|
|
// Clear the background
|
|
RECT rcFill = *prcClient;
|
|
rcFill.top = rcDraw.top;
|
|
FillRect(hdc, &rcFill, hbr);
|
|
}
|
|
|
|
// draw the text
|
|
_pMarkup->DrawText(hdc, &rcDraw);
|
|
|
|
if (hbr)
|
|
{
|
|
SelectObject(hdc, hbrOld);
|
|
}
|
|
|
|
if (NULL == hdcClient && hdc) // release DC if we acquired it.
|
|
{
|
|
ReleaseDC(_hwnd, hdc);
|
|
}
|
|
}
|
|
|
|
LRESULT CLink::SetItem(IN LITEM* pItem)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!_pMarkup)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (NULL == pItem ||
|
|
0 == (pItem->mask & LIF_ITEMINDEX))
|
|
{
|
|
return 0; //FEATURE: need to open up search keys to LIF_ITEMID and LIF_URL.
|
|
}
|
|
|
|
if (pItem->iLink>-1)
|
|
{
|
|
if (pItem->mask & LIF_STATE)
|
|
{
|
|
// Ask the markup callback to set state
|
|
hr = _pMarkup->SetState(pItem->iLink, pItem->stateMask, pItem->state);
|
|
|
|
// Deal with LIS_ENABLED
|
|
if (pItem->stateMask & LIS_ENABLED)
|
|
{
|
|
if (!IsWindowEnabled(_hwnd))
|
|
{
|
|
EnableWindow(_hwnd, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pItem->mask & LIF_ITEMID)
|
|
{
|
|
hr = _pMarkup->SetLinkText(pItem->iLink, MARKUPLINKTEXT_ID, pItem->szID);
|
|
}
|
|
|
|
if (pItem->mask & LIF_URL)
|
|
{
|
|
hr = _pMarkup->SetLinkText(pItem->iLink, MARKUPLINKTEXT_URL, pItem->szUrl);
|
|
}
|
|
}
|
|
|
|
UpdateTabstop();
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
LRESULT CLink::GetItem(OUT LITEM* pItem)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!_pMarkup)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (NULL == pItem || 0 == (pItem->mask & LIF_ITEMINDEX))
|
|
{
|
|
return 0; //FEATURE: need to open up search keys to LIF_ITEMID and LIF_URL.
|
|
}
|
|
|
|
if (pItem->iLink > -1)
|
|
{
|
|
if (pItem->mask & LIF_STATE)
|
|
{
|
|
hr = _pMarkup->GetState(pItem->iLink, pItem->stateMask, &pItem->state);
|
|
}
|
|
|
|
if (pItem->mask & LIF_ITEMID)
|
|
{
|
|
DWORD dwCch = ARRAYSIZE(pItem->szID);
|
|
hr = _pMarkup->GetLinkText(pItem->iLink, MARKUPLINKTEXT_ID, pItem->szID, &dwCch);
|
|
}
|
|
|
|
if (pItem->mask & LIF_URL)
|
|
{
|
|
DWORD dwCch = ARRAYSIZE(pItem->szUrl);
|
|
hr = _pMarkup->GetLinkText(pItem->iLink, MARKUPLINKTEXT_URL, pItem->szUrl, &dwCch);
|
|
}
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
LRESULT CLink::SendNotify(UINT nCode, int iLink, BOOL fGetLinkText) const
|
|
{
|
|
NMLINK nm;
|
|
ZeroMemory(&nm, sizeof(nm));
|
|
|
|
nm.hdr.hwndFrom = _hwnd;
|
|
nm.hdr.idFrom = (UINT_PTR)GetWindowLong(_hwnd, GWL_ID);
|
|
nm.hdr.code = nCode;
|
|
nm.item.iLink = iLink;
|
|
|
|
if (fGetLinkText)
|
|
{
|
|
DWORD dwCch;
|
|
|
|
dwCch = ARRAYSIZE(nm.item.szID);
|
|
_pMarkup->GetLinkText(iLink, MARKUPLINKTEXT_ID, nm.item.szID, &dwCch);
|
|
|
|
dwCch = ARRAYSIZE(nm.item.szUrl);
|
|
_pMarkup->GetLinkText(iLink, MARKUPLINKTEXT_URL, nm.item.szUrl, &dwCch);
|
|
}
|
|
|
|
return SendMessage(GetParent(_hwnd), WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
|
|
}
|
|
|
|
void CLink::UpdateTabstop()
|
|
{
|
|
if (_fTabStop)
|
|
SetWindowBits(_hwnd, GWL_STYLE, WS_TABSTOP, (_pMarkup->IsTabbable() == S_OK)?WS_TABSTOP:0);
|
|
}
|
|
|
|
|
|
LRESULT WINAPI CLink::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lRet = 0L;
|
|
CLink* pThis = NULL;
|
|
|
|
if (uMsg == WM_NCCREATE)
|
|
{
|
|
pThis = new CLink;
|
|
if (NULL == pThis)
|
|
{
|
|
TraceMsg(TF_WARNING, "CLink: Failed to allocate CLink in WM_NCCREATE.");
|
|
SetWindowPtr(hwnd, GWLP_USERDATA, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
pThis->_hwnd = hwnd;
|
|
SetWindowPtr(hwnd, GWLP_USERDATA, pThis);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
pThis = (CLink*)GetWindowPtr(hwnd, GWLP_USERDATA);
|
|
}
|
|
|
|
if (pThis != NULL)
|
|
{
|
|
ASSERT(pThis->_hwnd == hwnd);
|
|
|
|
switch(uMsg)
|
|
{
|
|
case WM_SETFONT:
|
|
{
|
|
if (pThis->_hfUnderline)
|
|
{
|
|
DeleteObject(pThis->_hfUnderline);
|
|
pThis->_hfUnderline = NULL;
|
|
}
|
|
|
|
pThis->_hfStatic = (HFONT)wParam;
|
|
if (pThis->_hfStatic)
|
|
pThis->_hfUnderline = CCCreateUnderlineFont(pThis->_hfStatic);
|
|
|
|
if (pThis->_pMarkup)
|
|
pThis->_pMarkup->SetFonts(pThis->_hfStatic, pThis->_hfUnderline);
|
|
}
|
|
break;
|
|
|
|
case WM_NCHITTEST:
|
|
{
|
|
POINT pt;
|
|
UINT idLink;
|
|
MakePoint(lParam, &pt);
|
|
MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1);
|
|
if (pThis->_pMarkup && pThis->_pMarkup->HitTest(pt, &idLink) == S_OK)
|
|
{
|
|
return HTCLIENT;
|
|
}
|
|
return HTTRANSPARENT;
|
|
}
|
|
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
if ((hdc = BeginPaint(pThis->_hwnd, &ps)) != NULL)
|
|
{
|
|
pThis->Paint(hdc);
|
|
EndPaint(pThis->_hwnd, &ps);
|
|
}
|
|
return lRet;
|
|
}
|
|
|
|
case WM_PRINTCLIENT:
|
|
pThis->Paint((HDC)wParam);
|
|
return lRet;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
{
|
|
WINDOWPOS* pwp = (WINDOWPOS*)lParam;
|
|
RECT rc;
|
|
GetClientRect(pThis->_hwnd, &rc);
|
|
if (0 == (pwp->flags & SWP_NOSIZE) &&
|
|
!(pwp->cx == RECTWIDTH(rc) &&
|
|
pwp->cy == RECTHEIGHT(rc)))
|
|
{
|
|
// FEATURE: implement LS_AUTOHEIGHT style by
|
|
// calling CalcIdealHeight() to compute the height for
|
|
// the given width.
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
pThis->Paint(NULL);
|
|
break;
|
|
}
|
|
|
|
case WM_CREATE:
|
|
{
|
|
if ((lRet = DefWindowProc(hwnd, uMsg, wParam, lParam)) == 0)
|
|
{
|
|
CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
|
|
|
|
if (FAILED(pThis->Initialize()))
|
|
return -1;
|
|
_InitializeUISTATE(hwnd, &pThis->_fKeyboardCues);
|
|
pThis->_fTabStop = (pcs->style & WS_TABSTOP);
|
|
pThis->_pMarkup->SetText(pcs->lpszName);
|
|
pThis->UpdateTabstop();
|
|
pThis->_bTransparent = (pcs->style & LWS_TRANSPARENT);
|
|
pThis->_bIgnoreReturn = (pcs->style & LWS_IGNORERETURN);
|
|
}
|
|
return lRet;
|
|
}
|
|
|
|
case WM_SETTEXT:
|
|
pThis->_pMarkup->SetText((LPCTSTR) lParam);
|
|
pThis->UpdateTabstop();
|
|
::InvalidateRect(pThis->_hwnd, NULL, FALSE);
|
|
break;
|
|
|
|
case WM_GETTEXT:
|
|
{
|
|
DWORD dwCch = (DWORD)wParam;
|
|
pThis->_pMarkup->GetText(TRUE, (LPTSTR)lParam, &dwCch);
|
|
return lstrlen((LPTSTR)lParam);
|
|
}
|
|
|
|
case WM_GETTEXTLENGTH:
|
|
{
|
|
DWORD dwCch;
|
|
pThis->_pMarkup->GetText(TRUE, NULL, &dwCch);
|
|
return dwCch-1; // return length in chars, not including NULL
|
|
}
|
|
|
|
case WM_SETFOCUS:
|
|
pThis->_pMarkup->SetFocus();
|
|
pThis->SendNotify(NM_SETFOCUS, NULL, NULL);
|
|
pThis->InvalidateRect(NULL);
|
|
|
|
return 0L;
|
|
|
|
case WM_KILLFOCUS:
|
|
pThis->_pMarkup->KillFocus();
|
|
return lRet;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
POINT pt;
|
|
MakePoint(lParam, &pt);
|
|
pThis->_pMarkup->OnButtonDown(pt);
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
{
|
|
POINT pt;
|
|
MakePoint(lParam, &pt);
|
|
pThis->_pMarkup->OnButtonUp(pt);
|
|
break;
|
|
}
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
POINT pt;
|
|
UINT idLink;
|
|
MakePoint(lParam, &pt);
|
|
if (pThis->_pMarkup->HitTest(pt, &idLink) == S_OK)
|
|
{
|
|
pThis->_pMarkup->SetLinkCursor();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LM_HITTEST: // wParam: n/a, lparam: PLITEM, ret: BOOL
|
|
{
|
|
LHITTESTINFO* phti = (LHITTESTINFO*)lParam;
|
|
if (phti)
|
|
{
|
|
if (SUCCEEDED(pThis->_pMarkup->HitTest(phti->pt, (UINT*)&phti->item.iLink)))
|
|
{
|
|
DWORD cch = ARRAYSIZE(phti->item.szID);
|
|
return (S_OK == pThis->_pMarkup->GetLinkText(phti->item.iLink, MARKUPLINKTEXT_ID, phti->item.szID, &cch));
|
|
}
|
|
}
|
|
return lRet;
|
|
}
|
|
|
|
case LM_SETITEM:
|
|
return pThis->SetItem((LITEM*)lParam);
|
|
|
|
case LM_GETITEM:
|
|
return pThis->GetItem((LITEM*)lParam);
|
|
|
|
case LM_GETIDEALHEIGHT: // wParam: cx, lparam: n/a, ret: cy
|
|
{
|
|
HDC hdc = GetDC(hwnd);
|
|
if (hdc)
|
|
{
|
|
RECT rc;
|
|
SetRect(&rc, 0, 0, (int)wParam, 0);
|
|
pThis->_pMarkup->CalcIdealSize(hdc, MARKUPSIZE_CALCHEIGHT, &rc);
|
|
ReleaseDC(hwnd, hdc);
|
|
return rc.bottom;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
case WM_NCDESTROY:
|
|
{
|
|
lRet = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
SetWindowPtr(hwnd, GWLP_USERDATA, 0);
|
|
|
|
if (pThis->_pMarkup)
|
|
pThis->_pMarkup->SetCallback(NULL);
|
|
|
|
if (pThis->_hfUnderline)
|
|
DeleteObject(pThis->_hfUnderline);
|
|
|
|
pThis->_hwnd = NULL;
|
|
pThis->Release();
|
|
return lRet;
|
|
}
|
|
|
|
case WM_GETDLGCODE:
|
|
{
|
|
MSG* pmsg;
|
|
lRet = DLGC_STATIC;
|
|
|
|
if ((pmsg = (MSG*)lParam))
|
|
{
|
|
if ((WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message))
|
|
{
|
|
switch(pmsg->wParam)
|
|
{
|
|
case VK_TAB:
|
|
if (pThis->_pMarkup->IsTabbable() == S_OK)
|
|
{
|
|
lRet |= DLGC_WANTTAB;
|
|
pThis->_fEatTabChar = TRUE;
|
|
}
|
|
break;
|
|
|
|
case VK_RETURN:
|
|
if (pThis->_bIgnoreReturn)
|
|
break;
|
|
// deliberate drop through..
|
|
case VK_SPACE:
|
|
lRet |= DLGC_WANTALLKEYS;
|
|
break;
|
|
}
|
|
}
|
|
else if (WM_CHAR == pmsg->message)
|
|
{
|
|
if (VK_RETURN == pmsg->wParam)
|
|
{
|
|
// Eat VK_RETURN WM_CHARs; we don't want
|
|
// Dialog manager to beep when IsDialogMessage gets it.
|
|
lRet |= DLGC_WANTMESSAGE;
|
|
}
|
|
else if (VK_TAB == pmsg->wParam &&
|
|
pThis->_fEatTabChar)
|
|
{
|
|
pThis->_fEatTabChar = FALSE;
|
|
lRet |= DLGC_WANTTAB;
|
|
}
|
|
}
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
pThis->_pMarkup->OnKeyDown((UINT)wParam);
|
|
case WM_KEYUP:
|
|
case WM_CHAR:
|
|
return lRet;
|
|
|
|
case WM_UPDATEUISTATE:
|
|
if (_HandleWM_UPDATEUISTATE(wParam, lParam, &pThis->_fKeyboardCues))
|
|
{
|
|
RedrawWindow(hwnd, NULL, NULL,
|
|
RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// oleacc defs thunked for WINVER < 0x0500
|
|
if (IsWM_GETOBJECT(uMsg) && OBJID_CLIENT == lParam)
|
|
{
|
|
return LresultFromObject(IID_IAccessible, wParam, SAFECAST(pThis, IAccessible*));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// CAccessibleBase IUnknown impl
|
|
STDMETHODIMP CAccessibleBase::QueryInterface(REFIID riid, void** ppvObj)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CAccessibleBase, IDispatch),
|
|
QITABENT(CAccessibleBase, IAccessible),
|
|
QITABENT(CAccessibleBase, IOleWindow),
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, (LPCQITAB)qit, riid, ppvObj);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CAccessibleBase::AddRef()
|
|
{
|
|
return InterlockedIncrement((LONG*)&_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CAccessibleBase::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement((LONG*)&_cRef);
|
|
if (cRef <= 0)
|
|
{
|
|
DllRelease();
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
// IOleWindow impl
|
|
STDMETHODIMP CAccessibleBase::GetWindow(HWND* phwnd)
|
|
{
|
|
*phwnd = _hwnd;
|
|
return IsWindow(_hwnd) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// CAccessibleBase IDispatch impl
|
|
//-------------------------------------------------------------------------//
|
|
|
|
static BOOL _accLoadTypeInfo(ITypeInfo** ppti)
|
|
{
|
|
ITypeLib* ptl;
|
|
HRESULT hr = LoadTypeLib(L"oleacc.dll", &ptl);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ptl->GetTypeInfoOfGuid(IID_IAccessible, ppti);
|
|
ATOMICRELEASE(ptl);
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::GetTypeInfoCount(UINT * pctinfo)
|
|
{
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (NULL == _ptiAcc && FAILED((hr = _accLoadTypeInfo(&_ptiAcc))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*pptinfo = _ptiAcc;
|
|
(*pptinfo)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::GetIDsOfNames(
|
|
REFIID riid,
|
|
OLECHAR** rgszNames,
|
|
UINT cNames,
|
|
LCID lcid, DISPID * rgdispid)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (IID_NULL != riid && IID_IAccessible != riid)
|
|
{
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
}
|
|
|
|
if (NULL == _ptiAcc && FAILED((hr = _accLoadTypeInfo(&_ptiAcc))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return _ptiAcc->GetIDsOfNames(rgszNames, cNames, rgdispid);
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::Invoke(
|
|
DISPID dispidMember,
|
|
REFIID riid,
|
|
LCID lcid,
|
|
WORD wFlags,
|
|
DISPPARAMS * pdispparams,
|
|
VARIANT * pvarResult,
|
|
EXCEPINFO * pexcepinfo,
|
|
UINT * puArgErr)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (IID_NULL != riid && IID_IAccessible != riid)
|
|
{
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
}
|
|
|
|
if (NULL == _ptiAcc && FAILED((hr = _accLoadTypeInfo(&_ptiAcc))))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return _ptiAcc->Invoke(this, dispidMember, wFlags, pdispparams,
|
|
pvarResult, pexcepinfo, puArgErr);
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accParent(IDispatch ** ppdispParent)
|
|
{
|
|
*ppdispParent = NULL;
|
|
if (IsWindow(_hwnd))
|
|
{
|
|
return AccessibleObjectFromWindow(_hwnd, OBJID_WINDOW,
|
|
IID_IDispatch, (void **)ppdispParent);
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accChildCount(long * pcChildren)
|
|
{
|
|
*pcChildren = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accChild(VARIANT varChildIndex, IDispatch ** ppdispChild)
|
|
{
|
|
*ppdispChild = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accValue(VARIANT varChild, BSTR* pbstrValue)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
*pbstrValue = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accDescription(VARIANT varChild, BSTR * pbstrDescription)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
*pbstrDescription = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accRole(VARIANT varChild, VARIANT *pvarRole)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
pvarRole->vt = VT_I4;
|
|
pvarRole->lVal = ROLE_SYSTEM_LINK;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accState(VARIANT varChild, VARIANT *pvarState)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
|
|
pvarState->vt = VT_I4;
|
|
pvarState->lVal = STATE_SYSTEM_DEFAULT ;
|
|
|
|
if (GetFocus() == _hwnd)
|
|
{
|
|
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
|
}
|
|
else if (IsWindowEnabled(_hwnd))
|
|
{
|
|
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
|
}
|
|
|
|
if (!IsWindowVisible(_hwnd))
|
|
{
|
|
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accHelp(VARIANT varChild, BSTR* pbstrHelp)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
*pbstrHelp = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accHelpTopic(BSTR* pbstrHelpFile, VARIANT varChild, long* pidTopic)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
*pbstrHelpFile = NULL;
|
|
*pidTopic = -1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accKeyboardShortcut(VARIANT varChild, BSTR* pbstrKeyboardShortcut)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
*pbstrKeyboardShortcut = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accFocus(VARIANT * pvarFocusChild)
|
|
{
|
|
HWND hwndFocus;
|
|
if ((hwndFocus = GetFocus()) == _hwnd || IsChild(_hwnd, hwndFocus))
|
|
{
|
|
pvarFocusChild->vt = VT_I4;
|
|
pvarFocusChild->lVal = CHILDID_SELF;
|
|
return S_OK;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accSelection(VARIANT * pvarSelectedChildren)
|
|
{
|
|
return get_accFocus(pvarSelectedChildren); // implemented same as focus.
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::get_accDefaultAction(VARIANT varChild, BSTR* pbstrDefaultAction)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
|
|
WCHAR wsz[128];
|
|
if (LoadStringW(HINST_THISDLL, GetDefaultActionStringID(), wsz, ARRAYSIZE(wsz)))
|
|
{
|
|
if (NULL == (*pbstrDefaultAction = SysAllocString(wsz)))
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::accSelect(long flagsSelect, VARIANT varChild)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
|
|
if (flagsSelect & SELFLAG_TAKEFOCUS)
|
|
{
|
|
SetFocus(_hwnd);
|
|
return S_OK;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(_hwnd, &rc);
|
|
*pxLeft = rc.left;
|
|
*pyTop = rc.top;
|
|
*pcxWidth = RECTWIDTH(rc);
|
|
*pcyHeight = RECTHEIGHT(rc);
|
|
|
|
varChild.vt = VT_I4;
|
|
varChild.lVal = CHILDID_SELF;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint)
|
|
{
|
|
pvarChildAtPoint->vt = VT_I4;
|
|
pvarChildAtPoint->lVal = CHILDID_SELF;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::put_accName(VARIANT varChild, BSTR bstrName)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAccessibleBase::put_accValue(VARIANT varChild, BSTR bstrValue)
|
|
{
|
|
VALIDATEACCCHILD(varChild, CHILDID_SELF, E_INVALIDARG);
|
|
return S_FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// KEYBOARDCUES helpes
|
|
BOOL _HandleWM_UPDATEUISTATE(
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam,
|
|
IN OUT UINT* puFlags)
|
|
{
|
|
UINT uFlags = *puFlags;
|
|
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case UIS_CLEAR:
|
|
*puFlags &= ~(HIWORD(wParam));
|
|
break;
|
|
|
|
case UIS_SET:
|
|
*puFlags |= HIWORD(wParam);
|
|
break;
|
|
}
|
|
|
|
return uFlags != *puFlags;
|
|
}
|
|
|
|
void _InitializeUISTATE(IN HWND hwnd, IN OUT UINT* puFlags)
|
|
{
|
|
HWND hwndParent = GetParent(hwnd);
|
|
*puFlags = (UINT)SendMessage(hwndParent, WM_QUERYUISTATE, 0, 0L);
|
|
}
|