#include "stdafx.h" #include "sfthost.h" #include "hostutil.h" #include "moreprog.h" #include #include "tray.h" // To get access to c_tray #include "rcids.h" // for IDM_PROGRAMS etc. #include // // Unfortunately, WTL #undef's SelectFont, so we have to define it again. // inline HFONT SelectFont(HDC hdc, HFONT hf) { return (HFONT)SelectObject(hdc, hf); } CMorePrograms::CMorePrograms(HWND hwnd) : _lRef(1), _hwnd(hwnd), _clrText(CLR_INVALID), _clrBk(CLR_INVALID) { } CMorePrograms::~CMorePrograms() { if (_hf) DeleteObject(_hf); if (_hfTTBold) DeleteObject(_hfTTBold); if (_hfMarlett) DeleteObject(_hfMarlett); ATOMICRELEASE(_pdth); ATOMICRELEASE(_psmPrograms); // Note that we do not need to clean up our HWNDs. // USER does that for us automatically. } // // Metrics changed -- update. // void CMorePrograms::_InitMetrics() { if (_hwndTT) { MakeMultilineTT(_hwndTT); // Disable/enable infotips based on user preference SendMessage(_hwndTT, TTM_ACTIVATE, ShowInfoTip(), 0); } } LRESULT CMorePrograms::_OnNCCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CMorePrograms *self = new CMorePrograms(hwnd); if (self) { SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)self); return ::DefWindowProc(hwnd, uMsg, wParam, lParam); } return FALSE; } // // Create an inner button that is exactly the right size. // // Height of inner button = height of text. // Width of inner button = full width. // // This allows us to let USER do most of the work of hit-testing and // focus rectangling. // LRESULT CMorePrograms::_OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { _hTheme = (PaneDataFromCreateStruct(lParam))->hTheme; if (!_hTheme) { _clrText = GetSysColor(COLOR_MENUTEXT); _clrBk = GetSysColor(COLOR_MENU); _hbrBk = GetSysColorBrush(COLOR_MENU); _colorHighlight = COLOR_HIGHLIGHT; _colorHighlightText = COLOR_HIGHLIGHTTEXT; // should match proglist values, in sfthost.cpp _margins.cxLeftWidth = 2*GetSystemMetrics(SM_CXEDGE); _margins.cxRightWidth = 2*GetSystemMetrics(SM_CXEDGE); } else { GetThemeColor(_hTheme, SPP_MOREPROGRAMS, 0, TMT_TEXTCOLOR, &_clrText ); _hbrBk = (HBRUSH) GetStockObject(HOLLOW_BRUSH); _colorHighlight = COLOR_MENUHILIGHT; _colorHighlightText = COLOR_HIGHLIGHTTEXT; // theme designer should make it so these margins match proglist's GetThemeMargins(_hTheme, NULL, SPP_MOREPROGRAMS, 0, TMT_CONTENTMARGINS, NULL, &_margins); // get the width of the arrow SIZE siz = { 0, 0 }; GetThemePartSize(_hTheme, NULL, SPP_MOREPROGRAMSARROW, 0, NULL, TS_TRUE, &siz); _cxArrow = siz.cx; } // If we're restricted, just create the window without doing any work // We still need to paint our background, so we can't just fail the create if(SHRestricted(REST_NOSMMOREPROGRAMS)) return TRUE; if (!LoadString(_Module.GetResourceInstance(), IDS_STARTPANE_MOREPROGRAMS, _szMessage, ARRAYSIZE(_szMessage))) { return FALSE; } // Find the accelerator _chMnem = CharUpperChar(SHFindMnemonic(_szMessage)); _hf = LoadControlFont(_hTheme, SPP_MOREPROGRAMS, FALSE, 0); // Get some information about the font the user has selected // and create a Marlett font at a matching size. TEXTMETRIC tm; HDC hdc = GetDC(hwnd); if (hdc) { HFONT hfPrev = SelectFont(hdc, _hf); if (hfPrev) { SIZE sizText; GetTextExtentPoint32(hdc, _szMessage, lstrlen(_szMessage), &sizText); _cxText = sizText.cx + GetSystemMetrics(SM_CXEDGE); // chevron should be a little right of the text if (GetTextMetrics(hdc, &tm)) { _tmAscent = tm.tmAscent; LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); lf.lfHeight = _tmAscent; lf.lfWeight = FW_NORMAL; lf.lfCharSet = SYMBOL_CHARSET; StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), TEXT("Marlett")); _hfMarlett = CreateFontIndirect(&lf); if (_hfMarlett) { SelectFont(hdc, _hfMarlett); if (GetTextMetrics(hdc, &tm)) { _tmAscentMarlett = tm.tmAscent; } if (0 == _cxArrow) // if we're not themed, or the GetThemePartSize failed, { // set the width of the Marlett arrow into _cxArrow GetTextExtentPoint32(hdc, GetLayout(hdc) & LAYOUT_RTL ? TEXT("w") : TEXT("8"), 1, &sizText); _cxArrow = sizText.cx; } } } SelectFont(hdc, hfPrev); } ReleaseDC(hwnd, hdc); } if (!_tmAscentMarlett) { return FALSE; } // This is the same large icon setting from proglist BOOL bLargeIcons = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, REGSTR_VAL_DV2_LARGEICONS, FALSE, TRUE /* default to large*/); RECT rc; GetClientRect(_hwnd, &rc); rc.left += _margins.cxLeftWidth; rc.right -= _margins.cxRightWidth; rc.top += _margins.cyTopHeight; rc.bottom -= _margins.cyBottomHeight; // Compute the text indent value, so more programs lines up with text on icons in programs list _cxTextIndent = (3 * GetSystemMetrics(SM_CXEDGE)) + // 2 between icon&text + 1 before icon GetSystemMetrics(bLargeIcons ? SM_CXICON : SM_CXSMICON); // truncate the indent, if the text won't fit in the given area if (_cxTextIndent > RECTWIDTH(rc) - (_cxText + _cxArrow)) { TraceMsg(TF_WARNING, "StartMenu: '%s' is %dpx, only room for %d- notify localizers!",_szMessage, _cxText, RECTWIDTH(rc)-(_cxArrow+_cxTextIndent)); _cxTextIndent = max(0, RECTWIDTH(rc) - (_cxText + _cxArrow)); } ASSERT(RECTHEIGHT(rc) > _tmAscent); _iTextCenterVal = (RECTHEIGHT(rc) - _tmAscent) / 2; // Do not set WS_TABSTOP or WS_GROUP; CMorePrograms handles that // BS_NOTIFY ensures that we get BN_SETFOCUS and BN_KILLFOCUS DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | BS_OWNERDRAW; _hwndButton = CreateWindowEx(0, TEXT("button"), _szMessage, dwStyle, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), _hwnd, (HMENU)IntToPtr(IDC_BUTTON), _Module.GetModuleInstance(), NULL); if (!_hwndButton) { return FALSE; } // // Don't freak out if this fails. It just means that the accessibility // stuff won't be perfect. // SetAccessibleSubclassWindow(_hwndButton); if (_hf) SetWindowFont(_hwndButton, _hf, FALSE); // Unlike the button itself, failure to create the tooltip is nonfatal. // only create the tooltip if auto-cascade is off if (!SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, REGSTR_VAL_DV2_AUTOCASCADE, FALSE, TRUE)) _hwndTT = _CreateTooltip(); _InitMetrics(); // We can survive if this fails to be created CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDropTargetHelper, &_pdth)); // // If this fails, no big whoop - you just don't get // drag/drop, boo hoo. // RegisterDragDrop(_hwndButton, this); return TRUE; } HWND CMorePrograms::_CreateTooltip() { DWORD dwStyle = WS_BORDER | TTS_NOPREFIX; HWND hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, dwStyle, 0, 0, 0, 0, _hwndButton, NULL, _Module.GetModuleInstance(), NULL); if (hwnd) { TCHAR szBuf[MAX_PATH]; TOOLINFO ti; ti.cbSize = sizeof(ti); ti.hwnd = _hwnd; ti.uId = reinterpret_cast(_hwndButton); ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS; 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. UINT ids = IDS_STARTPANE_MOREPROGRAMS_TIP; ti.lpszText = szBuf; if (LoadString(_Module.GetResourceInstance(), ids, szBuf, ARRAYSIZE(szBuf))) { SendMessage(hwnd, TTM_ADDTOOL, 0, reinterpret_cast(&ti)); } } return hwnd; } LRESULT CMorePrograms::_OnDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { RevokeDragDrop(_hwndButton); return DefWindowProc(hwnd, uMsg, wParam, lParam); } LRESULT CMorePrograms::_OnNCDestroy(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // WARNING! "this" might be invalid (if WM_NCCREATE failed), so // do not use any member variables! LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam); SetWindowPtr0(hwnd, 0); if (this) { this->Release(); } return lres; } LRESULT CMorePrograms::_OnCtlColorBtn(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc = reinterpret_cast(wParam); if (_clrText != CLR_INVALID) { SetTextColor(hdc, _clrText); } if (_clrBk != CLR_INVALID) { SetBkColor(hdc, _clrBk); } return reinterpret_cast(_hbrBk); } LRESULT CMorePrograms::_OnDrawItem(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPDRAWITEMSTRUCT pdis = reinterpret_cast(lParam); ASSERT(pdis->CtlType == ODT_BUTTON); ASSERT(pdis->CtlID == IDC_BUTTON); if (pdis->itemAction & (ODA_DRAWENTIRE | ODA_FOCUS)) { BOOL fRTLReading = GetLayout(pdis->hDC) & LAYOUT_RTL; UINT fuOptions = 0; if (fRTLReading) { fuOptions |= ETO_RTLREADING; } HFONT hfPrev = SelectFont(pdis->hDC, _hf); if (hfPrev) { BOOL fHot = (pdis->itemState & ODS_FOCUS) || _tmHoverStart; if (fHot) { // hot background FillRect(pdis->hDC, &pdis->rcItem, GetSysColorBrush(_colorHighlight)); SetTextColor(pdis->hDC, GetSysColor(_colorHighlightText)); } else if (_hTheme) { // Themed non-hot background = custom RECT rc; GetClientRect(hwnd, &rc); MapWindowRect(hwnd, pdis->hwndItem, &rc); DrawThemeBackground(_hTheme, pdis->hDC, SPP_MOREPROGRAMS, 0, &rc, 0); } else { // non-themed non-hot background FillRect(pdis->hDC, &pdis->rcItem, _hbrBk); } int iOldMode = SetBkMode(pdis->hDC, TRANSPARENT); // _cxTextIndent will move it in the current width of an icon (small or large), plus the space we add between an icon and the text pdis->rcItem.left += _cxTextIndent; UINT dtFlags = DT_VCENTER | DT_SINGLELINE | DT_EDITCONTROL; if (fRTLReading) { dtFlags |= DT_RTLREADING; } if (pdis->itemState & ODS_NOACCEL) { dtFlags |= DT_HIDEPREFIX; } DrawText(pdis->hDC, _szMessage, -1, &pdis->rcItem, dtFlags); RECT rc = pdis->rcItem; rc.left += _cxText; if (_hTheme) { if (_iTextCenterVal < 0) // text is taller than the bitmap rc.top += (-_iTextCenterVal); rc.right = rc.left + _cxArrow; // clip rectangle down to the minumum size... DrawThemeBackground(_hTheme, pdis->hDC, SPP_MOREPROGRAMSARROW, fHot ? SPS_HOT : 0, &rc, 0); } else { if (SelectFont(pdis->hDC, _hfMarlett)) { rc.top = rc.top + _tmAscent - _tmAscentMarlett + (_iTextCenterVal > 0 ? _iTextCenterVal : 0); TCHAR chOut = fRTLReading ? TEXT('w') : TEXT('8'); if (EVAL(!IsRectEmpty(&rc))) { ExtTextOut(pdis->hDC, rc.left, rc.top, fuOptions, &rc, &chOut, 1, NULL); rc.right = rc.left + _cxArrow; } } } _rcExclude = rc; _rcExclude.left -= _cxText; // includes the text in the exclusion rectangle. MapWindowRect(pdis->hwndItem, NULL, &_rcExclude); SetBkMode(pdis->hDC, iOldMode); SelectFont(pdis->hDC, hfPrev); } } // // Since we are emulating a menu item, we don't need to draw a // focus rectangle. // return TRUE; } void CMorePrograms::_TrackShellMenu(DWORD dwFlags) { // Pop the balloon tip and tell the Start Menu not to offer it any more _PopBalloon(); _SendNotify(_hwnd, SMN_SEENNEWITEMS); SMNTRACKSHELLMENU tsm; tsm.itemID = 0; tsm.dwFlags = dwFlags; if (!_psmPrograms) { CoCreateInstance(CLSID_PersonalStartMenu, NULL, CLSCTX_INPROC, IID_PPV_ARG(IShellMenu, &_psmPrograms)); } if (_psmPrograms) { tsm.psm = _psmPrograms; tsm.rcExclude = _rcExclude; HWND hwnd = _hwnd; _fMenuOpen = TRUE; _SendNotify(_hwnd, SMN_TRACKSHELLMENU, &tsm.hdr); } } LRESULT CMorePrograms::_OnCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_BUTTON: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case BN_CLICKED: _TrackShellMenu(0); break; } break; case IDC_KEYPRESS: _TrackShellMenu(MPPF_KEYBOARD | MPPF_INITIALSELECT); break; } return 0; } LRESULT CMorePrograms::_OnEraseBkgnd(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { RECT rc; GetClientRect(hwnd, &rc); if (_hTheme) { DrawThemeBackground(_hTheme, (HDC)wParam, SPP_MOREPROGRAMS, 0, &rc, 0); } else SHFillRectClr((HDC)wParam, &rc, _clrBk); return 0; } LRESULT CMorePrograms::_OnNotify(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPNMHDR pnm = reinterpret_cast(lParam); switch (pnm->code) { case SMN_FINDITEM: return _OnSMNFindItem(CONTAINING_RECORD(pnm, SMNDIALOGMESSAGE, hdr)); case SMN_SHOWNEWAPPSTIP: return _OnSMNShowNewAppsTip(CONTAINING_RECORD(pnm, SMNMBOOL, hdr)); case SMN_DISMISS: return _OnSMNDismiss(); case SMN_APPLYREGION: return HandleApplyRegion(_hwnd, _hTheme, (SMNMAPPLYREGION *)lParam, SPP_MOREPROGRAMS, 0); case SMN_SHELLMENUDISMISSED: _fMenuOpen = FALSE; return 0; } return 0; } LRESULT CMorePrograms::_OnSMNFindItem(PSMNDIALOGMESSAGE pdm) { if(SHRestricted(REST_NOSMMOREPROGRAMS)) return 0; switch (pdm->flags & SMNDM_FINDMASK) { // Life is simple if you have only one item -- all searches succeed! case SMNDM_FINDFIRST: case SMNDM_FINDLAST: case SMNDM_FINDNEAREST: case SMNDM_HITTEST: pdm->itemID = 0; return TRUE; case SMNDM_FINDFIRSTMATCH: { TCHAR tch = CharUpperChar((TCHAR)pdm->pmsg->wParam); if (tch == _chMnem) { pdm->itemID = 0; return TRUE; } } break; // not found case SMNDM_FINDNEXTMATCH: break; // there is only one item so there can't be a "next" case SMNDM_FINDNEXTARROW: if (pdm->flags & SMNDM_TRYCASCADE) { FORWARD_WM_COMMAND(_hwnd, IDC_KEYPRESS, NULL, 0, PostMessage); return TRUE; } break; // not found case SMNDM_INVOKECURRENTITEM: case SMNDM_OPENCASCADE: if (pdm->flags & SMNDM_KEYBOARD) { FORWARD_WM_COMMAND(_hwnd, IDC_KEYPRESS, NULL, 0, PostMessage); } else { FORWARD_WM_COMMAND(_hwnd, IDC_BUTTON, NULL, 0, PostMessage); } return TRUE; case SMNDM_FINDITEMID: return TRUE; default: ASSERT(!"Unknown SMNDM command"); break; } // // If not found, then tell caller what our orientation is (vertical) // and where the currently-selected item is. // pdm->flags |= SMNDM_VERTICAL; pdm->pt.x = 0; pdm->pt.y = 0; return FALSE; } // // The boolean parameter in the SMNMBOOL tells us whether to display or // hide the balloon tip. // LRESULT CMorePrograms::_OnSMNShowNewAppsTip(PSMNMBOOL psmb) { if(SHRestricted(REST_NOSMMOREPROGRAMS)) return 0; if (psmb->f) { if (_hwndTT) { SendMessage(_hwndTT, TTM_ACTIVATE, FALSE, 0); } if (!_hwndBalloon) { RECT rc; GetWindowRect(_hwndButton, &rc); if (!_hfTTBold) { NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(ncm); if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) { ncm.lfStatusFont.lfWeight = FW_BOLD; SHAdjustLOGFONT(&ncm.lfStatusFont); _hfTTBold = CreateFontIndirect(&ncm.lfStatusFont); } } _hwndBalloon = CreateBalloonTip(_hwnd, rc.left + _cxTextIndent + _cxText, (rc.top + rc.bottom)/2, _hfTTBold, 0, IDS_STARTPANE_MOREPROGRAMS_BALLOONTITLE); if (_hwndBalloon) { SetProp(_hwndBalloon, PROP_DV2_BALLOONTIP, DV2_BALLOONTIP_MOREPROG); } } } else { _PopBalloon(); } return 0; } void CMorePrograms::_PopBalloon() { if (_hwndBalloon) { DestroyWindow(_hwndBalloon); _hwndBalloon = NULL; } if (_hwndTT) { SendMessage(_hwndTT, TTM_ACTIVATE, TRUE, 0); } } LRESULT CMorePrograms::_OnSMNDismiss() { _PopBalloon(); return 0; } LRESULT CMorePrograms::_OnSysColorChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // update colors in classic mode if (!_hTheme) { _clrText = GetSysColor(COLOR_MENUTEXT); _clrBk = GetSysColor(COLOR_MENU); _hbrBk = GetSysColorBrush(COLOR_MENU); } SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL); return 0; } LRESULT CMorePrograms::_OnDisplayChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { _InitMetrics(); SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL); return 0; } LRESULT CMorePrograms::_OnSettingChange(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // _InitMetrics() is so cheap it's not worth getting too upset about // calling it too many times. _InitMetrics(); SHPropagateMessage(hwnd, uMsg, wParam, lParam, SPM_SEND | SPM_ONELEVEL); return 0; } LRESULT CMorePrograms::_OnContextMenu(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(SHRestricted(REST_NOSMMOREPROGRAMS)) return 0; if (IS_WM_CONTEXTMENU_KEYBOARD(lParam)) { RECT rc; GetWindowRect(_hwnd, &rc); lParam = MAKELPARAM(rc.left, rc.top); } c_tray.StartMenuContextMenu(_hwnd, (DWORD)lParam); return 0; } LRESULT CALLBACK CMorePrograms::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CMorePrograms *self = reinterpret_cast(GetWindowPtr(hwnd, GWLP_USERDATA)); switch (uMsg) { case WM_NCCREATE: return self->_OnNCCreate(hwnd, uMsg, wParam, lParam); case WM_CREATE: return self->_OnCreate(hwnd, uMsg, wParam, lParam); case WM_DESTROY: return self->_OnDestroy(hwnd, uMsg, wParam, lParam); case WM_NCDESTROY: return self->_OnNCDestroy(hwnd, uMsg, wParam, lParam); case WM_CTLCOLORBTN: return self->_OnCtlColorBtn(hwnd, uMsg, wParam, lParam); case WM_DRAWITEM: return self->_OnDrawItem(hwnd, uMsg, wParam, lParam); case WM_ERASEBKGND: return self->_OnEraseBkgnd(hwnd, uMsg, wParam, lParam); case WM_COMMAND: return self->_OnCommand(hwnd, uMsg, wParam, lParam); case WM_SYSCOLORCHANGE: return self->_OnSysColorChange(hwnd, uMsg, wParam, lParam); case WM_DISPLAYCHANGE: return self->_OnDisplayChange(hwnd, uMsg, wParam, lParam); case WM_SETTINGCHANGE: return self->_OnSettingChange(hwnd, uMsg, wParam, lParam); case WM_NOTIFY: return self->_OnNotify(hwnd, uMsg, wParam, lParam); case WM_CONTEXTMENU: return self->_OnContextMenu(hwnd, uMsg, wParam, lParam); } return ::DefWindowProc(hwnd, uMsg, wParam, lParam); } BOOL WINAPI MorePrograms_RegisterClass() { WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.style = CS_GLOBALCLASS; wc.lpfnWndProc = CMorePrograms::s_WndProc; wc.hInstance = _Module.GetModuleInstance(); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszClassName = WC_MOREPROGRAMS; return RegisterClassEx(&wc); } // We implement a minimal drop target so we can auto-open the More Programs // list when the user hovers over the More Programs button. // *** IUnknown *** HRESULT CMorePrograms::QueryInterface(REFIID riid, void * *ppvOut) { static const QITAB qit[] = { QITABENT(CMorePrograms, IDropTarget), QITABENT(CMorePrograms, IAccessible), QITABENT(CMorePrograms, IDispatch), // IAccessible derives from IDispatch { 0 }, }; return QISearch(this, qit, riid, ppvOut); } ULONG CMorePrograms::AddRef() { return InterlockedIncrement(&_lRef); } ULONG CMorePrograms::Release() { ASSERT( 0 != _lRef ); ULONG cRef = InterlockedDecrement(&_lRef); if ( 0 == cRef) { delete this; } return cRef; } // *** IDropTarget::DragEnter *** HRESULT CMorePrograms::DragEnter(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { POINT pt = { ptl.x, ptl.y }; if (_pdth) { _pdth->DragEnter(_hwnd, pdto, &pt, *pdwEffect); } // Remember when the hover started. _tmHoverStart = NonzeroGetTickCount(); InvalidateRect(_hwndButton, NULL, TRUE); // draw with drop highlight return DragOver(grfKeyState, ptl, pdwEffect); } // *** IDropTarget::DragOver *** HRESULT CMorePrograms::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { POINT pt = { ptl.x, ptl.y }; if (_pdth) { _pdth->DragOver(&pt, *pdwEffect); } // Hover time is 1 second, the same as the hard-coded value for the // Start Button. if (_tmHoverStart && GetTickCount() - _tmHoverStart > 1000) { _tmHoverStart = 0; FORWARD_WM_COMMAND(_hwnd, IDC_BUTTON, _hwndButton, BN_CLICKED, PostMessage); } *pdwEffect = DROPEFFECT_NONE; return S_OK; } // *** IDropTarget::DragLeave *** HRESULT CMorePrograms::DragLeave() { if (_pdth) { _pdth->DragLeave(); } _tmHoverStart = 0; InvalidateRect(_hwndButton, NULL, TRUE); // draw without drop highlight return S_OK; } // *** IDropTarget::Drop *** HRESULT CMorePrograms::Drop(IDataObject *pdto, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { POINT pt = { ptl.x, ptl.y }; if (_pdth) { _pdth->Drop(pdto, &pt, *pdwEffect); } _tmHoverStart = 0; InvalidateRect(_hwndButton, NULL, TRUE); // draw without drop highlight return S_OK; } //**************************************************************************** // // Accessibility // // // The default accessibility object reports buttons as // ROLE_SYSTEM_PUSHBUTTON, but we know that we are really a menu. // HRESULT CMorePrograms::get_accRole(VARIANT varChild, VARIANT *pvarRole) { HRESULT hr = CAccessible::get_accRole(varChild, pvarRole); if (SUCCEEDED(hr) && V_VT(pvarRole) == VT_I4) { switch (V_I4(pvarRole)) { case ROLE_SYSTEM_PUSHBUTTON: V_I4(pvarRole) = ROLE_SYSTEM_MENUITEM; break; } } return hr; } HRESULT CMorePrograms::get_accState(VARIANT varChild, VARIANT *pvarState) { HRESULT hr = CAccessible::get_accState(varChild, pvarState); if (SUCCEEDED(hr) && V_VT(pvarState) == VT_I4) { V_I4(pvarState) |= STATE_SYSTEM_HASPOPUP; } return hr; } HRESULT CMorePrograms::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut) { return CreateAcceleratorBSTR(_chMnem, pszKeyboardShortcut); } HRESULT CMorePrograms::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction) { DWORD dwRole = _fMenuOpen ? ACCSTR_CLOSE : ACCSTR_OPEN; return GetRoleString(dwRole, pszDefAction); } HRESULT CMorePrograms::accDoDefaultAction(VARIANT varChild) { if (_fMenuOpen) { _SendNotify(_hwnd, SMN_CANCELSHELLMENU); return S_OK; } else { return CAccessible::accDoDefaultAction(varChild); } }