#include "priv.h" #include "theater.h" #include "browbs.h" #include "resource.h" #include "legacy.h" #include "uxtheme.h" // needed for Margin. #include "mluisupp.h" #include "apithk.h" #define SUPERCLASS CBandSite TCHAR GetAccelerator(LPCTSTR psz, BOOL bUseDefault); #define ABS(x) (((x) < 0) ? -(x) : (x)) #define CX_TEXTOFFSET 6 #define CY_TEXTOFFSET 4 #define CX_TBOFFSET 1 #define CY_TBPADDING 1 #define CY_ETCH 2 #define CY_FLUFF 7 // *** IInputObject methods *** HRESULT CBrowserBandSite::HasFocusIO() { HWND hwnd = GetFocus(); if (hwnd && (hwnd == _hwndTB || hwnd == _hwndOptionsTB)) return S_OK; else return SUPERCLASS::HasFocusIO(); } // *** IDeskBarClient methods *** HRESULT CBrowserBandSite::SetModeDBC(DWORD dwMode) { if ((dwMode ^ _dwMode) & DBIF_VIEWMODE_VERTICAL) { // switching horizontal/vertical; need to toggle toolbar // since we hide toolbar for horizontal bars if (_hwndTBRebar) { if (dwMode & DBIF_VIEWMODE_VERTICAL) { ShowWindow(_hwndTBRebar, SW_SHOW); _fToolbar = _pCmdTarget ? TRUE : FALSE; } else { ShowWindow(_hwndTBRebar, SW_HIDE); _fToolbar = FALSE; } } } return SUPERCLASS::SetModeDBC(dwMode); } HRESULT CBrowserBandSite::TranslateAcceleratorIO(LPMSG lpMsg) { TraceMsg(TF_ACCESSIBILITY, "CBrowserBandSite::TranslateAcceleratorIO (hwnd=0x%08X) key=%d", _hwnd, lpMsg->wParam); HRESULT hr = S_FALSE; ASSERT((lpMsg->message >= WM_KEYFIRST) && (lpMsg->message <= WM_KEYLAST)); #if 0 //Disabled until a better key combination can be determined // check for Control-Shift arrow keys and resize if necessary if ((GetKeyState(VK_SHIFT) < 0) && (GetKeyState(VK_CONTROL) < 0)) { switch (lpMsg->wParam) { case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: IUnknown_Exec(_punkSite, &CGID_DeskBarClient, DBCID_RESIZE, (DWORD)lpMsg->wParam, NULL, NULL); return S_OK; } } #endif if (!g_fRunningOnNT && (lpMsg->message == WM_SYSCHAR)) { // See AnsiWparamToUnicode for why this character tweak is needed lpMsg->wParam = AnsiWparamToUnicode(lpMsg->wParam); } // Give toolbar a crack if (hr != S_OK && _hwndTB && SendMessage(_hwndTB, TB_TRANSLATEACCELERATOR, 0, (LPARAM)lpMsg)) return S_OK; else if (hr != S_OK && SendMessage(_hwndOptionsTB, TB_TRANSLATEACCELERATOR, 0, (LPARAM)lpMsg)) return S_OK; // to get a mapping, forward system characters to toolbar (if any) or xBar if (WM_SYSCHAR == lpMsg->message) { if (hr == S_OK) { return S_OK; } if ((NULL != _hwndTB) && (NULL != _pCmdTarget)) { UINT idBtn; if (SendMessage(_hwndTB, TB_MAPACCELERATOR, lpMsg->wParam, (LPARAM)&idBtn)) { TCHAR szButtonText[MAX_PATH]; // comctl says this one is the one, let's make sure we aren't getting // one of the unwanted "use the first letter" accelerators that it // will return. UINT cch = (UINT)SendMessage(_hwndTB, TB_GETBUTTONTEXT, idBtn, NULL); if (cch != 0 && cch < ARRAYSIZE(szButtonText)) { if ((SendMessage(_hwndTB, TB_GETBUTTONTEXT, idBtn, (LPARAM)szButtonText) > 0) && (GetAccelerator(szButtonText, FALSE) != (TCHAR)-1)) { // (tnoonan) - it feels kinda cheesy to send mouse messages, but // I don't know of a cleaner way which will accomplish what we // want (like deal with split buttons, mutually exclusive // buttons, etc.). RECT rc; SendMessage(_hwndTB, TB_GETRECT, idBtn, (LPARAM)&rc); SendMessage(_hwndTB, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(rc.left, rc.top)); SendMessage(_hwndTB, WM_LBUTTONUP, 0, MAKELONG(rc.left, rc.top)); hr = S_OK; } } } } } if (hr != S_OK) hr = SUPERCLASS::TranslateAcceleratorIO(lpMsg); return hr; } HRESULT CBrowserBandSite::_TrySetFocusTB(int iDir) { HRESULT hres = S_FALSE; if (_hwndTB) { int cBtns = (int) SendMessage(_hwndTB, TB_BUTTONCOUNT, 0, 0); if (cBtns > 0) { // Set focus on tb. This will also set the first button to hottracked, // generating a hot item change notify, but _OnHotItemChange will ignore // the notify as neither HICF_RESELECT, HICF_ARROWKEYS nor HICF_ACCELERATOR will be set. SetFocus(_hwndTB); // If going back, make rightmost button hottracked, // else make first button hottracked. int iHotPos = (iDir == -1) ? cBtns - 1 : 0; // Pass HICF_RESELECT so that if we're reselecting the same item, another notify // is generated, and so that the filter in _OnHotItemChange will let the notification // through (and pop down the chevron menu if necessary). SendMessage(_hwndTB, TB_SETHOTITEM2, iHotPos, HICF_RESELECT); hres = S_OK; } } return hres; } HRESULT CBrowserBandSite::_CycleFocusBS(LPMSG lpMsg) { // // Tab order goes: (out)->_hwndOptionsTB->bands->(out) // // The order is reversed when shift is pressed. // // When control is pressed, and we have focus (i.e., have already been tabbed // into), we reject focus since ctl-tab is supposed to tab between contexts. // // Once _hwndOptionsTB gets focus, user can arrow over to _hwndTB. If // that happens, replace _hwndOptionsTB with _hwndTB in order above. // BOOL fHasFocus = (HasFocusIO() == S_OK); ASSERT(fHasFocus || !_ptbActive); if (fHasFocus && IsVK_CtlTABCycler(lpMsg)) { // Bail on ctl-tab if one of our guys already has focus return S_FALSE; } HWND hwnd = GetFocus(); BOOL fHasTBFocus = (hwnd && (hwnd == _hwndTB || hwnd == _hwndOptionsTB)); BOOL fShift = (GetKeyState(VK_SHIFT) < 0); HRESULT hres = S_FALSE; // hidden options toolbar, bandsite cannot set focus to it (e.g. iBar) BOOL fStdExplorerBar = IsWindowVisible(_hwndOptionsTB); if (fHasTBFocus) { if (!fShift) hres = SUPERCLASS::_CycleFocusBS(lpMsg); } else { // Here, since !fHasTBFocus, fHasFocus => a band has focus if (fHasFocus || fShift || (!fHasFocus && !fStdExplorerBar)) hres = SUPERCLASS::_CycleFocusBS(lpMsg); if (hres != S_OK && (!fHasFocus || (fHasFocus && fShift))) { // try passing focus to options toolbar if visible; if (fStdExplorerBar) { SetFocus(_hwndOptionsTB); TraceMsg(TF_ACCESSIBILITY, "CBrowserBandSite::_CycleFocusBS (hwnd=0x%08X) setting focus to optionsTB (=0x%08X)", _hwnd, _hwndOptionsTB); hres = S_OK; } } } return hres; } // this class subclasses the CBandSite class and adds functionality specific to // being hosted in the browser.... // // it implements close as hide // it has its own title drawing void CBrowserBandSite::_OnCloseBand(DWORD dwBandID) { int iIndex = _BandIDToIndex(dwBandID); CBandItemData *pbid = _GetBandItem(iIndex); if (pbid) { _ShowBand(pbid, FALSE); if (_pct) { BOOL fShowing = FALSE; for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbidLoop = _GetBandItem(i); if (pbidLoop) { fShowing |= pbid->fShow; pbidLoop->Release(); } } if (!fShowing) { _pct->Exec(&CGID_DeskBarClient, DBCID_EMPTY, 0, NULL, NULL); } } pbid->Release(); } } // don't allow d/d of any band in here LRESULT CBrowserBandSite::_OnBeginDrag(NMREBAR* pnm) { return 1; } CBrowserBandSite::CBrowserBandSite() : CBandSite(NULL) { _dwBandIDCur = -1; } CBrowserBandSite::~CBrowserBandSite() { ATOMICRELEASE(_pCmdTarget); } HFONT CBrowserBandSite::_GetTitleFont(BOOL fForceRefresh) { if (_hfont && fForceRefresh) DeleteObject(_hfont); if (!_hfont || fForceRefresh) { // create our font to use for title & toolbar text // use A version for win9x compat NONCLIENTMETRICSA ncm; ncm.cbSize = sizeof(ncm); SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); if (!(_dwMode & DBIF_VIEWMODE_VERTICAL)) { // horizontal band w/ vertical caption, so rotate the font ncm.lfMenuFont.lfEscapement = 900; // rotate by 90 degrees ncm.lfMenuFont.lfOutPrecision = OUT_TT_ONLY_PRECIS; // TT can be rotated } _hfont = CreateFontIndirectA(&ncm.lfMenuFont); } return _hfont; } void CBrowserBandSite::_InitLayout() { // force update font _GetTitleFont(TRUE); // update toolbar font _UpdateToolbarFont(); // recalc title and toolbar heights _CalcHeights(); _UpdateLayout(); } void CBrowserBandSite::_UpdateAllBands(BOOL fBSOnly, BOOL fNoAutoSize) { if (!fBSOnly && !fNoAutoSize) _InitLayout(); SUPERCLASS::_UpdateAllBands(fBSOnly, fNoAutoSize); } HRESULT CBrowserBandSite::_Initialize(HWND hwndParent) { HRESULT hres = SUPERCLASS::_Initialize(hwndParent); SendMessage(_hwnd, CCM_SETUNICODEFORMAT, DLL_IS_UNICODE, 0); _CreateOptionsTB(); _InitLayout(); return hres; } void CBrowserBandSite::_CalcHeights() { // calc title height // calc height of captionless bandsites, too, needed to calc toolband height // HACKHACK: use height of 'All Folders' as standard title height TCHAR szTitle[64]; if (MLLoadStringW(IDS_TREETITLE, szTitle, ARRAYSIZE(szTitle))) { HDC hdc = GetDC(_hwnd); if (hdc) { HFONT hfont = _GetTitleFont(FALSE); HFONT hfontOld = (HFONT)SelectObject(hdc, hfont); int iLen = lstrlen(szTitle); SIZE size; GetTextExtentPoint32(hdc, szTitle, iLen, &size); _uTitle = size.cy; // make space for etch line + space _uTitle += CY_ETCH + CY_FLUFF; SelectObject(hdc, hfontOld); ReleaseDC(_hwnd, hdc); } } else { // no string; use a default height _uTitle = BROWSERBAR_TITLEHEIGHT; } // calc toolbar height _uToolbar = _uTitle + (2 * CY_TBPADDING) + CY_ETCH; if (_dwStyle & BSIS_NOCAPTION) { // rebar has no caption _uTitle = 0; } } void CBrowserBandSite::_UpdateToolbarFont() { if (_hwndTB && (_dwMode & DBIF_VIEWMODE_VERTICAL)) { // use same font for title and toolbar HFONT hfont = _GetTitleFont(FALSE); if (hfont) SendMessage(_hwndTB, WM_SETFONT, (WPARAM)hfont, TRUE); } } void CBrowserBandSite::_ShowBand(CBandItemData *pbid, BOOL fShow) { if (fShow && (_dwBandIDCur != pbid->dwBandID)) { _dwBandIDCur = pbid->dwBandID; _UpdateLayout(); } else if (!fShow && _dwBandIDCur == pbid->dwBandID) { _dwBandIDCur = -1; } SUPERCLASS::_ShowBand(pbid, fShow); } void CBrowserBandSite::_UpdateLayout() { // update toolbar button size if (_hwndTB && SendMessage(_hwndTB, TB_BUTTONCOUNT, 0, 0) > 0) { // try to set button height to title height LONG lSize = MAKELONG(0, _uTitle); SendMessage(_hwndTB, TB_SETBUTTONSIZE, 0, lSize); // see what toolbar actually gave us RECT rc; SendMessage(_hwndTB, TB_GETITEMRECT, 0, (LPARAM)&rc); // calc toolbar height (final version) _uToolbar = RECTHEIGHT(rc) + CY_ETCH + 2 * CY_TBPADDING; } // update header height for the current band if (_dwBandIDCur != -1) { REBARBANDINFO rbbi; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_HEADERSIZE; rbbi.cxHeader = _uTitle + (_fToolbar ? _uToolbar : 0); SendMessage(_hwnd, RB_SETBANDINFO, _BandIDToIndex(_dwBandIDCur), (LPARAM)&rbbi); } // update toolbar size _UpdateToolbarBand(); // reposition toolbars _PositionToolbars(NULL); } void CBrowserBandSite::_BandInfoFromBandItem(REBARBANDINFO *prbbi, CBandItemData *pbid, BOOL fBSOnly) { SUPERCLASS::_BandInfoFromBandItem(prbbi, pbid, fBSOnly); if (prbbi) { // we override header width so we can fit browbs's fancy ui (title, // toolbar, close & autohide buttons) in the band's header area. prbbi->cxHeader = _uTitle + (_fToolbar ? _uToolbar : 0); } } void CBrowserBandSite::_DrawEtchline(HDC hdc, LPRECT prc, int iOffset, BOOL fVertEtch) { RECT rc; CopyRect(&rc, prc); if (fVertEtch) { rc.left += iOffset - CY_ETCH; rc.right = rc.left + 1; } else { rc.top += iOffset - CY_ETCH; rc.bottom = rc.top + 1; } SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNSHADOW)); if (fVertEtch) { rc.left++; rc.right++; } else { rc.bottom++; rc.top++; } SHFillRectClr(hdc, &rc, GetSysColor(COLOR_BTNHILIGHT)); } LRESULT CBrowserBandSite::_OnCDNotify(LPNMCUSTOMDRAW pnm) { switch (pnm->dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; case CDDS_PREERASE: return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: if (!(_dwStyle & BSIS_NOCAPTION)) { // horz bar has vert caption and vice versa BOOL fVertCaption = (_dwMode&DBIF_VIEWMODE_VERTICAL) ? FALSE:TRUE; CBandItemData *pbid = (CBandItemData *)pnm->lItemlParam; if (pbid) { int iLen; HFONT hfont, hfontOld = NULL; LPCTSTR pszTitle; SIZE size; USES_CONVERSION; hfont = _GetTitleFont(FALSE); hfontOld = (HFONT)SelectObject(pnm->hdc, hfont); pszTitle = pbid->szTitle; iLen = lstrlen(pszTitle); GetTextExtentPoint32(pnm->hdc, pszTitle, iLen, &size); // center text inside caption and draw edge at bottom/right. if (!fVertCaption) { // vertical bar, has horizontal text int x = pnm->rc.left + CX_TEXTOFFSET; int y = pnm->rc.top + ((_uTitle - CY_ETCH) - size.cy) / 2; ExtTextOut(pnm->hdc, x, y, NULL, NULL, pszTitle, iLen, NULL); _DrawEtchline(pnm->hdc, &pnm->rc, RECTHEIGHT(pnm->rc), fVertCaption); if (_fToolbar) _DrawEtchline(pnm->hdc, &pnm->rc, _uTitle, fVertCaption); } else { // horizontal bar, has vertical text UINT nPrevAlign = SetTextAlign(pnm->hdc, TA_BOTTOM); int x = pnm->rc.right - ((_uTitle - CY_ETCH) - size.cy) / 2; int y = pnm->rc.bottom - CY_TEXTOFFSET; ExtTextOut(pnm->hdc, x, y, NULL, NULL, pszTitle, iLen, NULL); SetTextAlign(pnm->hdc, nPrevAlign); _DrawEtchline(pnm->hdc, &pnm->rc, RECTWIDTH(pnm->rc), fVertCaption); ASSERT(!_fToolbar); } if (hfontOld) SelectObject(pnm->hdc, hfontOld); } } return CDRF_SKIPDEFAULT; } return CDRF_DODEFAULT; } LRESULT CBrowserBandSite::_OnNotify(LPNMHDR pnm) { switch (pnm->idFrom) { case FCIDM_REBAR: switch (pnm->code) { case NM_CUSTOMDRAW: return _OnCDNotify((LPNMCUSTOMDRAW)pnm); case NM_NCHITTEST: { NMMOUSE *pnmMouse = (NMMOUSE*)pnm; RECT rc; GetClientRect(_hwnd, &rc); if (pnmMouse->dwItemSpec == (DWORD)-1) { Lchktrans: // // Edges are mirrored if the window is mirrored. [samera] // if (IS_WINDOW_RTL_MIRRORED(_hwnd)) { int iTmp = rc.right; rc.right = rc.left; rc.left = iTmp; } // gotta check all 4 edges or non-left-side bars (e.g. // commbar) won't work. // (we separate this into 2 checks to give a trace, // since the old code only checked the right side) if (pnmMouse->pt.x > rc.right) { return HTTRANSPARENT; } if (pnmMouse->pt.x < rc.left || pnmMouse->pt.y > rc.bottom || pnmMouse->pt.y < rc.top) { return HTTRANSPARENT; } } else if (pnmMouse->dwHitInfo == RBHT_CLIENT) { InflateRect(&rc, -(GetSystemMetrics(SM_CXFRAME)), -(GetSystemMetrics(SM_CYFRAME))); goto Lchktrans; } return SUPERCLASS::_OnNotify(pnm); } default: return SUPERCLASS::_OnNotify(pnm); } default: return SUPERCLASS::_OnNotify(pnm); } return 0; } IDropTarget* CBrowserBandSite::_WrapDropTargetForBand(IDropTarget* pdtBand) { pdtBand->AddRef(); return pdtBand; } HRESULT CBrowserBandSite::v_InternalQueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CBrowserBandSite, IExplorerToolbar), { 0 }, }; HRESULT hr; if (IsEqualIID(riid, IID_IDropTarget)) { *ppv = NULL; hr = E_NOINTERFACE; } else { hr = QISearch(this, qit, riid, ppv); if (FAILED(hr)) hr = SUPERCLASS::v_InternalQueryInterface(riid, ppv); } return hr; } DWORD CBrowserBandSite::_GetWindowStyle(DWORD *pdwExStyle) { *pdwExStyle = 0; return RBS_REGISTERDROP | RBS_VERTICALGRIPPER | RBS_VARHEIGHT | RBS_DBLCLKTOGGLE | WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_BORDER | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN; } // *** IOleCommandTarget *** HRESULT CBrowserBandSite::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { if (pguidCmdGroup == NULL) { } #ifdef UNIX // IEUNIX: Special case to handle the case where the band wants to // close itself. Used in Cache Warning pane (msgband.cpp) else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) { switch (nCmdID) { case SBCMDID_MSGBAND: { IDockingWindow * pdw; if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IDockingWindow, &pdw)))) { pdw->ShowDW((BOOL)nCmdexecopt); pdw->Release(); } } } } #endif else if (IsEqualGUID(CGID_Theater, *pguidCmdGroup)) { switch (nCmdID) { case THID_ACTIVATE: _fTheater = TRUE; SHSetWindowBits(_hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, 0); // fall through case THID_SETBROWSERBARAUTOHIDE: if (pvarargIn && pvarargIn->vt == VT_I4) _fNoAutoHide = !(pvarargIn->lVal); SendMessage(_hwndOptionsTB, TB_CHANGEBITMAP, IDM_AB_AUTOHIDE, _fNoAutoHide ? 2 : 0); break; case THID_DEACTIVATE: _fTheater = FALSE; SHSetWindowBits(_hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE); break; } SetWindowPos(_hwnd, NULL, 0,0,0,0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); _SizeOptionsTB(); return S_OK; } return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); } #define BBSC_REBAR 0x00000001 #define BBSC_TOOLBAR 0x00000002 void CBrowserBandSite::_CreateTBRebar() { ASSERT(!_hwndTBRebar); _hwndTBRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN, 0, 0, 100, 36, _hwnd, (HMENU) BBSC_REBAR, HINST_THISDLL, NULL); if (_hwndTBRebar) SendMessage(_hwndTBRebar, CCM_SETVERSION, COMCTL32_VERSION, 0); } void CBrowserBandSite::_InsertToolbarBand() { if (_hwndTBRebar && _hwndTB) { // Assert that we haven't added the toolbar band yet ASSERT(SendMessage(_hwndTBRebar, RB_GETBANDCOUNT, 0, 0) == 0); // Assert that we've calculated toolbar height ASSERT(_uToolbar); REBARBANDINFO rbbi; rbbi.cbSize = sizeof(REBARBANDINFO); rbbi.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE; // RBBIM_CHILD rbbi.hwndChild = _hwndTB; // RBBIM_CHILDSIZE rbbi.cxMinChild = 0; rbbi.cyMinChild = _uToolbar - (CY_ETCH + 2 * CY_TBPADDING); // RBBIM_STYLE rbbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON; SendMessage(_hwndTBRebar, RB_INSERTBAND, -1, (LPARAM)&rbbi); } } void CBrowserBandSite::_UpdateToolbarBand() { if (_hwndTBRebar && _hwndTB) { // Assert that we've added the toolbar band ASSERT(SendMessage(_hwndTBRebar, RB_GETBANDCOUNT, 0, 0) == 1); // Assert that we've calculated toolbar height ASSERT(_uToolbar); REBARBANDINFO rbbi; rbbi.cbSize = sizeof(REBARBANDINFO); rbbi.fMask = RBBIM_CHILDSIZE; SIZE size = {0, _uToolbar}; if (SendMessage(_hwndTB, TB_GETIDEALSIZE, FALSE, (LPARAM)&size)) { // RBBIM_IDEALSIZE rbbi.fMask |= RBBIM_IDEALSIZE; rbbi.cxIdeal = size.cx; } // RBBIM_CHILDSIZE rbbi.cxMinChild = 0; rbbi.cyMinChild = _uToolbar - (CY_ETCH + 2 * CY_TBPADDING); SendMessage(_hwndTBRebar, RB_SETBANDINFO, 0, (LPARAM)&rbbi); } } void CBrowserBandSite::_CreateTB() { ASSERT(!_hwndTB); // Create a rebar too so we get the chevron _CreateTBRebar(); if (_hwndTBRebar) { _hwndTB = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | TBSTYLE_LIST | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE, 0, 0, 0, 0, _hwndTBRebar, (HMENU) BBSC_TOOLBAR, HINST_THISDLL, NULL); } if (_hwndTB) { SendMessage(_hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(_hwndTB, CCM_SETVERSION, COMCTL32_VERSION, 0); // FEATURE: use TBSTYLE_EX_HIDECLIPPEDBUTTONS here? looks kinda goofy so i'm leaving it out for now. SendMessage(_hwndTB, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_MIXEDBUTTONS); SendMessage(_hwndTB, TB_SETMAXTEXTROWS, 1, 0L); _UpdateToolbarFont(); _InsertToolbarBand(); } } void CBrowserBandSite::_RemoveAllButtons() { if (!_hwndTB || !_hwndTBRebar) return; ShowWindow(_hwndTBRebar, SW_HIDE); _fToolbar = FALSE; INT_PTR nCount = SendMessage(_hwndTB, TB_BUTTONCOUNT, 0, 0L); while (nCount-- > 0) SendMessage(_hwndTB, TB_DELETEBUTTON, nCount, 0L); _UpdateLayout(); } void CBrowserBandSite::_Close() { ATOMICRELEASE(_pCmdTarget); // // Destroying _hwndTBRebar will take care of _hwndTB too // ASSERT(!_hwndTB || IsChild(_hwndTBRebar, _hwndTB)); DESTROY_OBJ_WITH_HANDLE(_hwndTBRebar, DestroyWindow); DESTROY_OBJ_WITH_HANDLE(_hwndOptionsTB, DestroyWindow); DESTROY_OBJ_WITH_HANDLE(_hfont, DeleteObject); SUPERCLASS::_Close(); } LRESULT CBrowserBandSite::_OnHotItemChange(LPNMTBHOTITEM pnmtb) { LRESULT lres = 0; // We might want to drop down the chevron menu if the hot item change // flags has these characteristics: // // - not HICF_LEAVING, since if HICF_LEAVING, the hot item should instead wrap to _hwndClose // - and not HICF_MOUSE, since we only drop down on keyboard hot item change // - HICF_ACCELERATOR | HICF_ARROWKEYS, since we only drop down on keyboard hot item change // - or HICF_RESELECT, since we force a reselect in _TrySetFocusTB // if (!(pnmtb->dwFlags & (HICF_LEAVING | HICF_MOUSE)) && (pnmtb->dwFlags & (HICF_RESELECT | HICF_ACCELERATOR | HICF_ARROWKEYS))) { // Check to see if new hot button is clipped. If it is, // then we pop down the chevron menu. RECT rc; GetClientRect(_hwndTB, &rc); int iButton = (int)SendMessage(_hwndTB, TB_COMMANDTOINDEX, pnmtb->idNew, 0); if (SHIsButtonObscured(_hwndTB, &rc, iButton)) { // Clear hot item SendMessage(_hwndTB, TB_SETHOTITEM, -1, 0); // Figure out whether to highlight first or last item in menu UINT uSelect; int cButtons = (int)SendMessage(_hwndTB, TB_BUTTONCOUNT, 0, 0); if (iButton == cButtons - 1) uSelect = DBPC_SELECTLAST; else uSelect = DBPC_SELECTFIRST; // Pop it down SendMessage(_hwndTBRebar, RB_PUSHCHEVRON, 0, uSelect); lres = 1; } } return lres; } LRESULT CBrowserBandSite::_OnNotifyBBS(LPNMHDR pnm) { switch (pnm->code) { case TBN_DROPDOWN: if (EVAL(_pCmdTarget)) { LPNMTOOLBAR pnmtoolbar = (LPNMTOOLBAR)pnm; VARIANTARG var; RECT rc = pnmtoolbar->rcButton; var.vt = VT_I4; MapWindowPoints(_hwndTB, HWND_DESKTOP, (LPPOINT)&rc, 2); var.lVal = MAKELONG(rc.left, rc.bottom); _pCmdTarget->Exec(&_guidButtonGroup, pnmtoolbar->iItem, OLECMDEXECOPT_PROMPTUSER, &var, NULL); } break; case TBN_WRAPHOTITEM: { LPNMTBWRAPHOTITEM pnmwh = (LPNMTBWRAPHOTITEM) pnm; if (pnmwh->nReason & HICF_ARROWKEYS) { if (pnm->hwndFrom == _hwndOptionsTB) { if (_TrySetFocusTB(pnmwh->iDir) != S_OK) return 0; } else { ASSERT(pnm->hwndFrom == _hwndTB); SetFocus(_hwndOptionsTB); } return 1; } } break; case TBN_HOTITEMCHANGE: if (pnm->hwndFrom == _hwndTB) return _OnHotItemChange((LPNMTBHOTITEM)pnm); break; case TBN_GETINFOTIP: // [scotthan] We'll ask the toolbar owner for tip text via // IOleCommandTarget::QueryStatus, like we do w/ defview for itbar buttons if (_pCmdTarget && pnm->hwndFrom == _hwndTB) { NMTBGETINFOTIP* pgit = (NMTBGETINFOTIP*)pnm ; OLECMDTEXTV cmdtv; OLECMDTEXT *pcmdText = &cmdtv; pcmdText->cwBuf = MAX_TOOLTIP_STRING; pcmdText->cmdtextf = OLECMDTEXTF_NAME; pcmdText->cwActual = 0; OLECMD rgcmd = {pgit->iItem, 0}; HRESULT hr = _pCmdTarget->QueryStatus(&_guidButtonGroup, 1, &rgcmd, pcmdText); if (SUCCEEDED(hr) && (pcmdText->cwActual)) { SHUnicodeToTChar(pcmdText->rgwz, pgit->pszText, pgit->cchTextMax); return 1; } } break ; case RBN_CHEVRONPUSHED: LPNMREBARCHEVRON pnmch = (LPNMREBARCHEVRON) pnm; MapWindowPoints(pnmch->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&pnmch->rc, 2); ToolbarMenu_Popup(_hwnd, &pnmch->rc, NULL, _hwndTB, 0, (DWORD)pnmch->lParamNM); return 1; } return 0; } // *** IWinEventHandler *** HRESULT CBrowserBandSite::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { switch (uMsg) { case WM_COMMAND: { HWND hwndControl = GET_WM_COMMAND_HWND(wParam, lParam); UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam); if (hwndControl && hwndControl == _hwndTB) { if (EVAL(_pCmdTarget)) { RECT rc; VARIANTARG var; var.vt = VT_I4; SendMessage(_hwndTB, TB_GETRECT, idCmd, (LPARAM)&rc); MapWindowPoints(_hwndTB, HWND_DESKTOP, (LPPOINT)&rc, 2); var.lVal = MAKELONG(rc.left, rc.bottom); _pCmdTarget->Exec(&_guidButtonGroup, idCmd, 0, &var, NULL); } return S_OK; } else if (hwndControl == _hwndOptionsTB) { switch (idCmd) { case IDM_AB_CLOSE: IUnknown_Exec(_punkSite, &CGID_DeskBarClient, DBCID_EMPTY, 0, NULL, NULL); break; case IDM_AB_AUTOHIDE: { VARIANTARG v = {0}; v.vt = VT_I4; v.lVal = _fNoAutoHide; IUnknown_Exec(_punkSite, &CGID_Theater, THID_SETBROWSERBARAUTOHIDE, 0, &v, NULL); break; } } return S_OK; } } break; case WM_NOTIFY: { LPNMHDR pnm = (LPNMHDR)lParam; if (pnm && (pnm->hwndFrom == _hwndTB || pnm->hwndFrom == _hwndOptionsTB || pnm->hwndFrom == _hwndTBRebar)) { *plres = _OnNotifyBBS(pnm); return S_OK; } } break; case WM_SIZE: { POINT pt = {LOWORD(lParam), HIWORD(lParam)}; _PositionToolbars(&pt); } break; } return SUPERCLASS::OnWinEvent(hwnd, uMsg, wParam, lParam, plres); } HRESULT CBrowserBandSite::IsWindowOwner(HWND hwnd) { if (hwnd && (hwnd == _hwndTB) || (hwnd == _hwndOptionsTB) || (hwnd == _hwndTBRebar)) return S_OK; return SUPERCLASS::IsWindowOwner(hwnd); } // *** IBandSite *** HRESULT CBrowserBandSite::SetBandSiteInfo(const BANDSITEINFO * pbsinfo) { // recompute our layout if vertical viewmode is changing BOOL fUpdate = ((pbsinfo->dwMask & BSIM_STATE) && ((pbsinfo->dwState ^ _dwMode) & DBIF_VIEWMODE_VERTICAL)); // ...or if caption is turned on or off BOOL fCaptionStyleChanged = ( (pbsinfo->dwMask & BSIM_STYLE) && ((pbsinfo->dwStyle ^ _dwStyle) & BSIS_NOCAPTION)); HRESULT hres = SUPERCLASS::SetBandSiteInfo(pbsinfo); if (fCaptionStyleChanged && _hwndOptionsTB) { if (_fToolbar) { // don't know if "new" band requires buttons or not, expect band to add buttons again if needed _RemoveAllButtons(); } // show or hide close/hide toolbar; is always created since bandsites get reused! ::ShowWindow(_hwndOptionsTB, (_dwStyle & BSIS_NOCAPTION) ? SW_HIDE : SW_SHOW); } if (fUpdate || fCaptionStyleChanged) { _InitLayout(); } return hres; } // *** IExplorerToolbar *** HRESULT CBrowserBandSite::SetCommandTarget(IUnknown* punkCmdTarget, const GUID* pguidButtonGroup, DWORD dwFlags) { HRESULT hres = S_OK; BOOL fRemoveButtons = TRUE; // dwFlags is not used ASSERT(!(dwFlags)); ATOMICRELEASE(_pCmdTarget); if (punkCmdTarget && pguidButtonGroup) { hres = punkCmdTarget->QueryInterface(IID_IOleCommandTarget, (void**)&(_pCmdTarget)); if (!_hwndTB) { _CreateTB(); } else if (_fToolbar && IsEqualGUID(_guidButtonGroup, *pguidButtonGroup)) { fRemoveButtons = FALSE; hres = S_FALSE; } _guidButtonGroup = *pguidButtonGroup; } else ASSERT(!punkCmdTarget); if (fRemoveButtons) _RemoveAllButtons(); ASSERT(SUCCEEDED(hres)); return hres; } // client should have already called AddString HRESULT CBrowserBandSite::AddButtons(const GUID* pguidButtonGroup, UINT nButtons, const TBBUTTON* lpButtons) { if (!_hwndTB || !nButtons) return E_FAIL; _RemoveAllButtons(); if (SendMessage(_hwndTB, TB_ADDBUTTONS, nButtons, (LPARAM)lpButtons)) { ShowWindow(_hwndTBRebar, SW_SHOW); _fToolbar = TRUE; _UpdateLayout(); return S_OK; } return E_FAIL; } HRESULT CBrowserBandSite::AddString(const GUID* pguidButtonGroup, HINSTANCE hInst, UINT_PTR uiResID, LRESULT* pOffset) { *pOffset = -1; if (!_hwndTB) return E_FAIL; *pOffset = SendMessage(_hwndTB, TB_ADDSTRING, (WPARAM)hInst, (LPARAM)uiResID); if (*pOffset != -1) return S_OK; return E_FAIL; } HRESULT CBrowserBandSite::GetButton(const GUID* pguidButtonGroup, UINT uiCommand, LPTBBUTTON lpButton) { if (!_hwndTB) return E_FAIL; UINT_PTR uiIndex = SendMessage(_hwndTB, TB_COMMANDTOINDEX, uiCommand, 0L); if (SendMessage(_hwndTB, TB_GETBUTTON, uiIndex, (LPARAM)lpButton)) return S_OK; return E_FAIL; } HRESULT CBrowserBandSite::GetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT* pfState) { if (!_hwndTB) return E_FAIL; *pfState = (UINT)SendMessage(_hwndTB, TB_GETSTATE, uiCommand, 0L); return S_OK; } HRESULT CBrowserBandSite::SetState(const GUID* pguidButtonGroup, UINT uiCommand, UINT fState) { if (!_hwndTB) return E_FAIL; UINT_PTR uiState = SendMessage(_hwndTB, TB_GETSTATE, uiCommand, NULL); uiState ^= fState; if (uiState) SendMessage(_hwndTB, TB_SETSTATE, uiCommand, (LPARAM)fState); return S_OK; } HRESULT CBrowserBandSite::SetImageList( const GUID* pguidCmdGroup, HIMAGELIST himlNormal, HIMAGELIST himlHot, HIMAGELIST himlDisabled) { if (IsEqualGUID(*pguidCmdGroup, _guidButtonGroup)) { SendMessage(_hwndTB, TB_SETIMAGELIST, 0, (LPARAM)himlNormal); SendMessage(_hwndTB, TB_SETHOTIMAGELIST, 0, (LPARAM)himlHot); SendMessage(_hwndTB, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)himlDisabled); } return S_OK; }; BYTE TBStateFromIndex(HWND hwnd, int iIndex) { TBBUTTONINFO tbbi; tbbi.cbSize = sizeof(TBBUTTONINFO); tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE; tbbi.fsState = 0; SendMessage(hwnd, TB_GETBUTTONINFO, iIndex, (LPARAM)&tbbi); return tbbi.fsState; } int CBrowserBandSite::_ContextMenuHittest(LPARAM lParam, POINT* ppt) { if (lParam == (LPARAM)-1) { // // Keyboard activation. If one of our toolbars has // focus, and it has a hottracked button, put up the // context menu below that button. // HWND hwnd = GetFocus(); if (hwnd && (hwnd == _hwndTB || hwnd == _hwndOptionsTB)) { INT_PTR iHot = SendMessage(hwnd, TB_GETHOTITEM, 0, 0); if (iHot == -1) { // couldn't find a hot item, just use the first visible button iHot = 0; while (TBSTATE_HIDDEN & TBStateFromIndex(hwnd, (int)iHot)) iHot++; ASSERT(iHot < SendMessage(hwnd, TB_BUTTONCOUNT, 0, 0)); } RECT rc; SendMessage(hwnd, TB_GETITEMRECT, iHot, (LPARAM)&rc); ppt->x = rc.left; ppt->y = rc.bottom; MapWindowPoints(hwnd, HWND_DESKTOP, ppt, 1); return -1; } } return SUPERCLASS::_ContextMenuHittest(lParam, ppt); } HMENU CBrowserBandSite::_LoadContextMenu() { HMENU hmenu = SUPERCLASS::_LoadContextMenu(); DeleteMenu(hmenu, BSIDM_SHOWTITLEBAND, MF_BYCOMMAND); return hmenu; } // create the options (close/hide) buttons void CBrowserBandSite::_CreateOptionsTB() { // create toolbar for close/hide button always since band site is reused // for captionless band sites (iBar), this toolbar is only hidden _hwndOptionsTB = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOMOVEY | CCS_NOPARENTALIGN | CCS_NORESIZE, 0, 0, 30, 18, _hwnd, 0, HINST_THISDLL, NULL); _PrepareOptionsTB(); } // init as toolbar and load bitmaps void CBrowserBandSite::_PrepareOptionsTB() { if (_hwndOptionsTB) { static const TBBUTTON c_tb[] = { { 0, IDM_AB_AUTOHIDE, TBSTATE_ENABLED, TBSTYLE_CHECK, {0,0}, 0, 0 }, { 1, IDM_AB_CLOSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0,0}, 0, 1 } }; SendMessage(_hwndOptionsTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); SendMessage(_hwndOptionsTB, CCM_SETVERSION, COMCTL32_VERSION, 0); SendMessage(_hwndOptionsTB, TB_SETBITMAPSIZE, 0, (LPARAM) MAKELONG(13, 11)); TBADDBITMAP tbab = { HINST_THISDLL, IDB_BROWSERTOOLBAR }; SendMessage(_hwndOptionsTB, TB_ADDBITMAP, 3, (LPARAM)&tbab); LONG_PTR cbOffset = SendMessage(_hwndOptionsTB, TB_ADDSTRING, (WPARAM)MLGetHinst(), (LPARAM)IDS_BANDSITE_CLOSE_LABELS); TBBUTTON tb[ARRAYSIZE(c_tb)]; UpdateButtonArray(tb, c_tb, ARRAYSIZE(c_tb), cbOffset); SendMessage(_hwndOptionsTB, TB_SETMAXTEXTROWS, 0, 0); SendMessage(_hwndOptionsTB, TB_ADDBUTTONS, ARRAYSIZE(tb), (LPARAM)tb); SendMessage(_hwndOptionsTB, TB_SETINDENT, (WPARAM)0, 0); _SizeOptionsTB(); } } void CBrowserBandSite::_PositionToolbars(LPPOINT ppt) { RECT rc; if (ppt) { rc.left = 0; rc.right = ppt->x; } else { GetClientRect(_hwnd, &rc); } if (_hwndOptionsTB) { // always put the close restore at the top right of the floater window int x; if (_dwMode & DBIF_VIEWMODE_VERTICAL) { RECT rcTB; GetWindowRect(_hwndOptionsTB, &rcTB); x = rc.right - RECTWIDTH(rcTB) - 1; } else { x = rc.left; } MARGINS mBorders = {0, 1, 0, 0}; // 1 mimics old behavior downlevel. Comctl32_GetBandMargins(_hwnd, &mBorders); SetWindowPos(_hwndOptionsTB, HWND_TOP, x - mBorders.cxRightWidth, mBorders.cyTopHeight, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); } if (_hwndTBRebar) { if (_fToolbar) { // toolbar goes on its own line below title SetWindowPos(_hwndTBRebar, HWND_TOP, rc.left + CX_TBOFFSET, _uTitle + CX_TBOFFSET, rc.right - 2 * CX_TBOFFSET, _uToolbar, SWP_SHOWWINDOW); } else { ASSERT(!IsWindowVisible(_hwndTBRebar)); } } } // sets the size of the toolbar. if we're in theater mode, we need to show the pushpin. // otherwise just show the close void CBrowserBandSite::_SizeOptionsTB() { RECT rc; GetWindowRect(_hwndOptionsTB, &rc); LRESULT lButtonSize = SendMessage(_hwndOptionsTB, TB_GETBUTTONSIZE, 0, 0L); SetWindowPos(_hwndOptionsTB, NULL, 0, 0, LOWORD(lButtonSize) * (_fTheater ? 2 : 1), RECTHEIGHT(rc), SWP_NOMOVE | SWP_NOACTIVATE); DWORD_PTR dwState = SendMessage(_hwndOptionsTB, TB_GETSTATE, IDM_AB_AUTOHIDE, 0); dwState &= ~(TBSTATE_HIDDEN | TBSTATE_CHECKED); if (!_fTheater) dwState |= TBSTATE_HIDDEN; if (_fNoAutoHide) dwState |= TBSTATE_CHECKED; SendMessage(_hwndOptionsTB, TB_SETSTATE, IDM_AB_AUTOHIDE, dwState); _PositionToolbars(NULL); }