417 lines
13 KiB
C++
417 lines
13 KiB
C++
#include "stdafx.h"
|
|
#include "hostutil.h"
|
|
|
|
#define DEFAULT_BALLOON_TIMEOUT (10*1000) // 10 seconds
|
|
|
|
LRESULT CALLBACK BalloonTipSubclassProc(
|
|
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
|
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_TIMER:
|
|
// Our autodismiss timer
|
|
if (uIdSubclass == wParam)
|
|
{
|
|
KillTimer(hwnd, wParam);
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
|
|
// On a settings change, recompute our size and margins
|
|
case WM_SETTINGCHANGE:
|
|
MakeMultilineTT(hwnd);
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
RemoveWindowSubclass(hwnd, BalloonTipSubclassProc, uIdSubclass);
|
|
break;
|
|
}
|
|
|
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
//
|
|
// A "fire and forget" balloon tip. Tell it where to go, what font
|
|
// to use, and what to say, and it pops up and times out.
|
|
//
|
|
HWND CreateBalloonTip(HWND hwndOwner, int x, int y, HFONT hf,
|
|
UINT idsTitle, UINT idsText)
|
|
{
|
|
DWORD dwStyle = TTS_ALWAYSTIP | TTS_BALLOON | TTS_NOPREFIX;
|
|
|
|
HWND hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, dwStyle,
|
|
0, 0, 0, 0,
|
|
hwndOwner, NULL,
|
|
_Module.GetModuleInstance(), NULL);
|
|
if (hwnd)
|
|
{
|
|
MakeMultilineTT(hwnd);
|
|
|
|
TCHAR szBuf[MAX_PATH];
|
|
TOOLINFO ti;
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = hwndOwner;
|
|
ti.uId = reinterpret_cast<UINT_PTR>(hwndOwner);
|
|
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRACK;
|
|
ti.hinst = _Module.GetResourceInstance();
|
|
|
|
// We can't use MAKEINTRESOURCE because that allows only up to 80
|
|
// characters for text, and our text can be longer than that.
|
|
ti.lpszText = szBuf;
|
|
if (LoadString(_Module.GetResourceInstance(), idsText, szBuf, ARRAYSIZE(szBuf)))
|
|
{
|
|
SendMessage(hwnd, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&ti));
|
|
|
|
if (idsTitle &&
|
|
LoadString(_Module.GetResourceInstance(), idsTitle, szBuf, ARRAYSIZE(szBuf)))
|
|
{
|
|
SendMessage(hwnd, TTM_SETTITLE, TTI_INFO, reinterpret_cast<LPARAM>(szBuf));
|
|
}
|
|
|
|
SendMessage(hwnd, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
|
|
|
|
if (hf)
|
|
{
|
|
SetWindowFont(hwnd, hf, FALSE);
|
|
}
|
|
|
|
SendMessage(hwnd, TTM_TRACKACTIVATE, TRUE, reinterpret_cast<LPARAM>(&ti));
|
|
|
|
// Set the autodismiss timer
|
|
if (SetWindowSubclass(hwnd, BalloonTipSubclassProc, (UINT_PTR)hwndOwner, 0))
|
|
{
|
|
SetTimer(hwnd, (UINT_PTR)hwndOwner, DEFAULT_BALLOON_TIMEOUT, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
// Make the tooltip control multiline (infotip or balloon tip).
|
|
// The size computations are the same ones that comctl32 uses
|
|
// for listview and treeview infotips.
|
|
|
|
void MakeMultilineTT(HWND hwndTT)
|
|
{
|
|
HWND hwndOwner = GetWindow(hwndTT, GW_OWNER);
|
|
HDC hdc = GetDC(hwndOwner);
|
|
if (hdc)
|
|
{
|
|
int iWidth = MulDiv(GetDeviceCaps(hdc, LOGPIXELSX), 300, 72);
|
|
int iMaxWidth = GetDeviceCaps(hdc, HORZRES) * 3 / 4;
|
|
SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, min(iWidth, iMaxWidth));
|
|
|
|
static const RECT rcMargin = {4, 4, 4, 4};
|
|
SendMessage(hwndTT, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
|
|
|
|
ReleaseDC(hwndOwner, hdc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CPropBagFromReg::CPropBagFromReg(HKEY hk)
|
|
{
|
|
_cref = 1;
|
|
_hk = hk;
|
|
};
|
|
CPropBagFromReg::~CPropBagFromReg()
|
|
{
|
|
RegCloseKey(_hk);
|
|
}
|
|
|
|
STDMETHODIMP CPropBagFromReg::QueryInterface(REFIID riid, PVOID *ppvObject)
|
|
{
|
|
if (IsEqualIID(riid, IID_IPropertyBag))
|
|
*ppvObject = (IPropertyBag *)this;
|
|
else if (IsEqualIID(riid, IID_IUnknown))
|
|
*ppvObject = this;
|
|
else
|
|
{
|
|
*ppvObject = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG CPropBagFromReg::AddRef(void)
|
|
{
|
|
return ++_cref; // on the stack
|
|
}
|
|
ULONG CPropBagFromReg::Release(void)
|
|
{
|
|
if (--_cref)
|
|
return _cref;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP CPropBagFromReg::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
|
|
{
|
|
VARTYPE vtDesired = pVar->vt;
|
|
|
|
WCHAR szTmp[100];
|
|
DWORD cb = sizeof(szTmp);
|
|
DWORD dwType;
|
|
if (ERROR_SUCCESS == RegQueryValueExW(_hk, pszPropName, NULL, &dwType, (LPBYTE)szTmp, &cb) && (REG_SZ==dwType))
|
|
{
|
|
// TODO - use dwType to set the vt properly
|
|
pVar->bstrVal = SysAllocString(szTmp);
|
|
if (pVar->bstrVal)
|
|
{
|
|
pVar->vt = VT_BSTR;
|
|
return VariantChangeTypeForRead(pVar, vtDesired);
|
|
}
|
|
else
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
HRESULT CreatePropBagFromReg(LPCTSTR pszKey, IPropertyBag**pppb)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
*pppb = NULL;
|
|
|
|
// Try current user 1st, if that fails, fall back to localmachine
|
|
HKEY hk;
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, pszKey, NULL, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hk)
|
|
|| ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszKey, NULL, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hk))
|
|
{
|
|
CPropBagFromReg* pcpbfi = new CPropBagFromReg(hk);
|
|
if (pcpbfi)
|
|
{
|
|
hr = pcpbfi->QueryInterface(IID_IPropertyBag, (void**) pppb);
|
|
pcpbfi->Release();
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey(hk);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
};
|
|
|
|
BOOL RectFromStrW(LPCWSTR pwsz, RECT *pr)
|
|
{
|
|
pr->left = StrToIntW(pwsz);
|
|
pwsz = StrChrW(pwsz, L',');
|
|
if (!pwsz)
|
|
return FALSE;
|
|
pr->top = StrToIntW(++pwsz);
|
|
pwsz = StrChrW(pwsz, L',');
|
|
if (!pwsz)
|
|
return FALSE;
|
|
pr->right = StrToIntW(++pwsz);
|
|
pwsz = StrChrW(pwsz, L',');
|
|
if (!pwsz)
|
|
return FALSE;
|
|
pr->bottom = StrToIntW(++pwsz);
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT HandleApplyRegion(HWND hwnd, HTHEME hTheme,
|
|
PSMNMAPPLYREGION par, int iPartId, int iStateId)
|
|
{
|
|
if (hTheme)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
// Map to caller's coordinates
|
|
MapWindowRect(NULL, par->hdr.hwndFrom, &rc);
|
|
|
|
HRGN hrgn;
|
|
if (SUCCEEDED(GetThemeBackgroundRegion(hTheme, NULL, iPartId, iStateId, &rc, &hrgn)) && hrgn)
|
|
{
|
|
// Replace our window rectangle with the region
|
|
HRGN hrgnRect = CreateRectRgnIndirect(&rc);
|
|
if (hrgnRect)
|
|
{
|
|
// We want to take par->hrgn, subtract hrgnRect and add hrgn.
|
|
// But we want to do this with a single operation to par->hrgn
|
|
// so we don't end up with a corrupted region on low memory failure.
|
|
// So we do
|
|
//
|
|
// par->hrgn ^= hrgnRect ^ hrgn.
|
|
//
|
|
// If hrgnRect ^ hrgn == NULLREGION then the background
|
|
// does not want to customize the rectangle so we can just
|
|
// leave par->hrgn alone.
|
|
|
|
int iResult = CombineRgn(hrgn, hrgn, hrgnRect, RGN_XOR);
|
|
if (iResult != ERROR && iResult != NULLREGION)
|
|
{
|
|
CombineRgn(par->hrgn, par->hrgn, hrgn, RGN_XOR);
|
|
}
|
|
DeleteObject(hrgnRect);
|
|
}
|
|
DeleteObject(hrgn);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//****************************************************************************
|
|
//
|
|
// CAccessible - Most of this class is just forwarders
|
|
|
|
#define ACCESSIBILITY_FORWARD(fn, typedargs, args) \
|
|
HRESULT CAccessible::fn typedargs \
|
|
{ \
|
|
return _paccInner->fn args; \
|
|
}
|
|
|
|
ACCESSIBILITY_FORWARD(get_accParent,
|
|
(IDispatch **ppdispParent),
|
|
(ppdispParent))
|
|
ACCESSIBILITY_FORWARD(GetTypeInfoCount,
|
|
(UINT *pctinfo),
|
|
(pctinfo))
|
|
ACCESSIBILITY_FORWARD(GetTypeInfo,
|
|
(UINT itinfo, LCID lcid, ITypeInfo **pptinfo),
|
|
(itinfo, lcid, pptinfo))
|
|
ACCESSIBILITY_FORWARD(GetIDsOfNames,
|
|
(REFIID riid, OLECHAR **rgszNames, UINT cNames,
|
|
LCID lcid, DISPID *rgdispid),
|
|
(riid, rgszNames, cNames, lcid, rgdispid))
|
|
ACCESSIBILITY_FORWARD(Invoke,
|
|
(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
|
|
DISPPARAMS *pdispparams, VARIANT *pvarResult,
|
|
EXCEPINFO *pexcepinfo, UINT *puArgErr),
|
|
(dispidMember, riid, lcid, wFlags,
|
|
pdispparams, pvarResult,
|
|
pexcepinfo, puArgErr))
|
|
ACCESSIBILITY_FORWARD(get_accChildCount,
|
|
(long *pChildCount),
|
|
(pChildCount))
|
|
ACCESSIBILITY_FORWARD(get_accChild,
|
|
(VARIANT varChildIndex, IDispatch **ppdispChild),
|
|
(varChildIndex, ppdispChild))
|
|
ACCESSIBILITY_FORWARD(get_accName,
|
|
(VARIANT varChild, BSTR *pszName),
|
|
(varChild, pszName))
|
|
ACCESSIBILITY_FORWARD(get_accValue,
|
|
(VARIANT varChild, BSTR *pszValue),
|
|
(varChild, pszValue))
|
|
ACCESSIBILITY_FORWARD(get_accDescription,
|
|
(VARIANT varChild, BSTR *pszDescription),
|
|
(varChild, pszDescription))
|
|
ACCESSIBILITY_FORWARD(get_accRole,
|
|
(VARIANT varChild, VARIANT *pvarRole),
|
|
(varChild, pvarRole))
|
|
ACCESSIBILITY_FORWARD(get_accState,
|
|
(VARIANT varChild, VARIANT *pvarState),
|
|
(varChild, pvarState))
|
|
ACCESSIBILITY_FORWARD(get_accHelp,
|
|
(VARIANT varChild, BSTR *pszHelp),
|
|
(varChild, pszHelp))
|
|
ACCESSIBILITY_FORWARD(get_accHelpTopic,
|
|
(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic),
|
|
(pszHelpFile, varChild, pidTopic))
|
|
ACCESSIBILITY_FORWARD(get_accKeyboardShortcut,
|
|
(VARIANT varChild, BSTR *pszKeyboardShortcut),
|
|
(varChild, pszKeyboardShortcut))
|
|
ACCESSIBILITY_FORWARD(get_accFocus,
|
|
(VARIANT *pvarFocusChild),
|
|
(pvarFocusChild))
|
|
ACCESSIBILITY_FORWARD(get_accSelection,
|
|
(VARIANT *pvarSelectedChildren),
|
|
(pvarSelectedChildren))
|
|
ACCESSIBILITY_FORWARD(get_accDefaultAction,
|
|
(VARIANT varChild, BSTR *pszDefaultAction),
|
|
(varChild, pszDefaultAction))
|
|
ACCESSIBILITY_FORWARD(accSelect,
|
|
(long flagsSelect, VARIANT varChild),
|
|
(flagsSelect, varChild))
|
|
ACCESSIBILITY_FORWARD(accLocation,
|
|
(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild),
|
|
(pxLeft, pyTop, pcxWidth, pcyHeight, varChild))
|
|
ACCESSIBILITY_FORWARD(accNavigate,
|
|
(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt),
|
|
(navDir, varStart, pvarEndUpAt))
|
|
ACCESSIBILITY_FORWARD(accHitTest,
|
|
(long xLeft, long yTop, VARIANT *pvarChildAtPoint),
|
|
(xLeft, yTop, pvarChildAtPoint))
|
|
ACCESSIBILITY_FORWARD(accDoDefaultAction,
|
|
(VARIANT varChild),
|
|
(varChild));
|
|
ACCESSIBILITY_FORWARD(put_accName,
|
|
(VARIANT varChild, BSTR szName),
|
|
(varChild, szName))
|
|
ACCESSIBILITY_FORWARD(put_accValue,
|
|
(VARIANT varChild, BSTR pszValue),
|
|
(varChild, pszValue));
|
|
|
|
|
|
LRESULT CALLBACK CAccessible::s_SubclassProc(
|
|
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
|
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
|
|
{
|
|
CAccessible *self = reinterpret_cast<CAccessible *>(dwRefData);
|
|
|
|
switch (uMsg)
|
|
{
|
|
|
|
case WM_GETOBJECT:
|
|
if (lParam == OBJID_CLIENT) {
|
|
HRESULT hr;
|
|
|
|
// Create the accessibility object for the inner listview if we haven't already
|
|
// We forward nearly all calls to the inner IAccessible.
|
|
if (!self->_paccInner)
|
|
{
|
|
hr = CreateStdAccessibleObject(hwnd, (DWORD)lParam, IID_PPV_ARG(IAccessible, &self->_paccInner));
|
|
} else {
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
return LresultFromObject(IID_IAccessible, wParam, SAFECAST(self, IAccessible *));
|
|
} else {
|
|
return hr;
|
|
}
|
|
};
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
RemoveWindowSubclass(hwnd, s_SubclassProc, 0);
|
|
break;
|
|
|
|
|
|
}
|
|
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
HRESULT CAccessible::GetRoleString(DWORD dwRole, BSTR *pbsOut)
|
|
{
|
|
*pbsOut = NULL;
|
|
|
|
WCHAR szBuf[MAX_PATH];
|
|
if (GetRoleTextW(dwRole, szBuf, ARRAYSIZE(szBuf)))
|
|
{
|
|
*pbsOut = SysAllocString(szBuf);
|
|
}
|
|
|
|
return *pbsOut ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT CAccessible::CreateAcceleratorBSTR(TCHAR tch, BSTR *pbsOut)
|
|
{
|
|
TCHAR sz[2] = { tch, 0 };
|
|
*pbsOut = SysAllocString(sz);
|
|
return *pbsOut ? S_OK : E_OUTOFMEMORY;
|
|
}
|