//-------------------------------------------------------------------------// // 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 #include #include #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); }