#include "priv.h" #include "sccls.h" #include #define WANT_CBANDSITE_CLASS #include "bandsite.h" #include "bandobj.h" #include "caggunk.h" #include "droptgt.h" #include "resource.h" #include "bands.h" #include "legacy.h" #include "apithk.h" #include "mluisupp.h" #define TF_BANDDD 0x00400000 #define DM_INIT 0 // #define DM_PERSIST 0 // trace IPS::Load, ::Save, etc. #define DM_MENU 0 // menu code #define DM_DRAG 0 // drag&drop #define DM_FOCUS 0 // focus #define DM_PERF 0 // perf tune #define DM_PERF2 0 // perf tune (verbose) #define IDM_DRAGDROP 1 #define ISMOVEDDISABLED(dwBandID) ((S_OK == _IsRestricted(dwBandID, RA_MOVE, BAND_ADMIN_NOMOVE)) ? TRUE : FALSE) #define ISDDCLOSEDISABLED(dwBandID) ((S_OK == _IsRestricted(dwBandID, RA_DRAG, BAND_ADMIN_NODDCLOSE)) ? TRUE : FALSE) // drag state (NOTE from dockbar.h) #define DRAG_NIL 0 // nil #define DRAG_MOVE 1 // moving #define DRAG_SIZE 2 // sizing typedef struct { UINT cx; UINT fStyle; UINT cxMinChild; UINT cyMinChild; UINT cyIntegral; UINT cyMaxChild; UINT cyChild; } PERSISTBANDINFO_V3; typedef struct { UINT cx; UINT fStyle; UINT cxMinChild; // UNUSED. reclaim! UINT cyMinChild; UINT cyIntegral; // UNUSED UINT cyMaxChild; // UNUSED. UINT cyChild; DWORD dwAdminSettings; BITBOOL fNoTitle:1; } PERSISTBANDINFO; #define RBBIM_XPERSIST (RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE) #ifdef DEBUG extern unsigned long DbStreamTell(IStream *pstm); #else #define DbStreamTell(pstm) ((ULONG) 0) #endif UINT _FixMenuIndex(HMENU hmenu, UINT indexMenu) { UINT i; i = GetMenuItemCount(hmenu); if (indexMenu > i) indexMenu = i; return indexMenu; } #define SUPERCLASS CAggregatedUnknown HRESULT CBandSite::v_InternalQueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { // perf: last tuned 980728 QITABENT(CBandSite, IBandSite), // IID_IBandSite QITABENT(CBandSite, IInputObject), // IID_IInputObject QITABENT(CBandSite, IServiceProvider), // IID_IServiceProvider QITABENT(CBandSite, IOleCommandTarget), // IID_IOleCommandTarget QITABENTMULTI(CBandSite, IOleWindow, IDeskBarClient), // IID_IOleWindow QITABENT(CBandSite, IWinEventHandler), // IID_IWinEventHandler QITABENT(CBandSite, IInputObjectSite), // IID_IInputObjectSite QITABENT(CBandSite, IDeskBarClient), // IID_IDeskBarClient QITABENTMULTI(CBandSite, IPersist, IPersistStream), // rare IID_IPersist QITABENT(CBandSite, IPersistStream), // rare IID_IPersistStream QITABENT(CBandSite, IBandSiteHelper), // rare IBandSiteHelper QITABENT(CBandSite, IDropTarget), // rare IID_IDropTarget { 0 }, }; return QISearch(this, qit, riid, ppvObj); } DWORD _SetDataListFlags(IUnknown *punk, DWORD dwMaskBits, DWORD dwValue) { DWORD dw = 0; IShellLinkDataList *pdl; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &pdl)))) { pdl->GetFlags(&dw); dw = (dw & ~dwMaskBits) | (dwValue & dwMaskBits); pdl->SetFlags(dw); pdl->Release(); } return dw; } ///// impl of IServiceProvider HRESULT CBandSite::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { HRESULT hres = E_FAIL; *ppvObj = NULL; // assume error if (IsEqualIID(guidService, SID_IBandProxy)) { hres = QueryService_SID_IBandProxy(_punkSite, riid, &_pbp, ppvObj); if(!_pbp) { // We need to create it ourselves since our parent couldn't help ASSERT(FALSE == _fCreatedBandProxy); hres = CreateIBandProxyAndSetSite(_punkSite, riid, &_pbp, ppvObj); if(_pbp) { ASSERT(S_OK == hres); _fCreatedBandProxy = TRUE; } } } else if (IsEqualIID(guidService, SID_ITopViewHost)) { return QueryInterface(riid, ppvObj); } else if (IsEqualIID(guidService, IID_IBandSite)) { // It is common for bands to save/load pidls for persistence. // CShellLink is a robust way to do this, so let's share one // among all the bands. // // NOTE: This is shared between bands, so if you request it // you must complete your use of it within the scope of your // function call! // if (IsEqualIID(riid, IID_IShellLinkA) || IsEqualIID(riid, IID_IShellLinkW)) { if (NULL == _plink) CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &_plink)); if (_plink) { // we know that the bandsite is going to be pointing to local folders // to avoid a perf hit we get in loading the LINKINFO.DLL we explictly // disable that functionality here. _SetDataListFlags(_plink, SLDF_FORCE_NO_LINKINFO, SLDF_FORCE_NO_LINKINFO); hres = _plink->QueryInterface(riid, ppvObj); } } } else { hres = IUnknown_QueryService(_punkSite, guidService, riid, ppvObj); } return hres; } HRESULT CBandSite::GetWindow(HWND * lphwnd) { *lphwnd = _hwnd; return *lphwnd ? S_OK : E_FAIL; } CBandSite::CBandSite(IUnknown* punkAgg) : SUPERCLASS(punkAgg) { DWORD dwData = 0; DWORD dwSize = sizeof(dwData); // We assume this object was zero inited. ASSERT(!_pbp); ASSERT(FALSE == _fCreatedBandProxy); SHRegGetUSValue(SZ_REGKEY_GLOBALADMINSETTINGS, SZ_REGVALUE_GLOBALADMINSETTINGS, NULL, &dwData, &dwSize, FALSE, NULL, 0); if (IsFlagSet(dwData, BAND_ADMIN_ADMINMACHINE)) _fIEAKInstalled = TRUE; else _fIEAKInstalled = FALSE; _dwStyle = BSIS_AUTOGRIPPER; // // We check whether or not this succeeded in CBandSite::_Initialize // _QueryOuterInterface(IID_PPV_ARG(IBandSite, &_pbsOuter)); DllAddRef(); } void CBandSite::_ReleaseBandItemData(CBandItemData *pbid, int iIndex) { if (pbid->pdb) { pbid->pdb->CloseDW(0); if (-1 != iIndex) { REBARBANDINFO rbbi; // The band's hwnd is typically destroyed in CloseDW rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_CHILD | RBBIM_LPARAM; rbbi.hwndChild = NULL; rbbi.lParam = NULL; SendMessage(_hwnd, RB_SETBANDINFO, iIndex, (LPARAM) &rbbi); } // this is called from remove and the destroy. IUnknown_SetSite(pbid->pdb, NULL); ATOMICRELEASE(pbid->pdb); } if (pbid->pweh == _pwehCache) ATOMICRELEASE(_pwehCache); ATOMICRELEASE(pbid->pweh); pbid->Release(); } CBandSite::~CBandSite() { ATOMICRELEASE(_pdtobj); if(_pbp && _fCreatedBandProxy) _pbp->SetSite(NULL); ATOMICRELEASE(_pbp); ATOMICRELEASE(_pwehCache); _CacheActiveBand(NULL); _Close(); SetDeskBarSite(NULL); if (_plink) _plink->Release(); RELEASEOUTERINTERFACE(_pbsOuter); DllRelease(); } //*** _IsBandDeleteable -- // ENTRY/EXIT // idBand band ID // ret TRUE if deletable, o.w. FALSE (also FALSE on bogus band) BOOL CBandSite::_IsBandDeleteable(DWORD dwBandID) { DWORD dwState; if (FAILED(_pbsOuter->QueryBand(dwBandID, NULL, &dwState, NULL, 0)) || (dwState & BSSF_UNDELETEABLE)) { return FALSE; } ASSERT(dwBandID != (DWORD)-1); // make sure QueryBand catches this return TRUE; } DWORD CBandSite::_GetAdminSettings(DWORD dwBandID) { DWORD dwAdminSettings = BAND_ADMIN_NORMAL; CBandItemData *pbid = _GetBandItem(_BandIDToIndex(dwBandID)); if (pbid) { dwAdminSettings = pbid->dwAdminSettings; pbid->Release(); } return dwAdminSettings; } void CBandSite::_SetAdminSettings(DWORD dwBandID, DWORD dwNewAdminSettings) { CBandItemData *pbid = _GetBandItem(_BandIDToIndex(dwBandID)); if (pbid) { pbid->dwAdminSettings = dwNewAdminSettings; pbid->Release(); } } //*** CBandSite::IBandSite::* { /*---------------------------------------------------------- Purpose: IBandSite::EnumBands method */ HRESULT CBandSite::EnumBands(UINT uBand, DWORD* pdwBandID) { ASSERT((NULL == pdwBandID && (UINT)-1 == uBand) || IS_VALID_WRITE_PTR(pdwBandID, DWORD)); if (uBand == (UINT)-1) return _GetBandItemCount(); // query count CBandItemData *pbid = _GetBandItem(uBand); if (pbid) { *pdwBandID = pbid->dwBandID; pbid->Release(); return S_OK; } return E_FAIL; } /*---------------------------------------------------------- Purpose: IBandSite::QueryBand method */ HRESULT CBandSite::QueryBand(DWORD dwBandID, IDeskBand** ppstb, DWORD* pdwState, LPWSTR pszName, int cchName) { ASSERT(NULL == ppstb || IS_VALID_WRITE_PTR(ppstb, IDeskBand)); ASSERT(NULL == pdwState || IS_VALID_WRITE_PTR(pdwState, DWORD)); ASSERT(NULL == pszName || IS_VALID_WRITE_BUFFER(pszName, WCHAR, cchName)); if (ppstb) *ppstb = NULL; CBandItemData *pbid = _GetBandItemDataStructByID(dwBandID); if (!pbid) return E_FAIL; if (pszName) { StrCpyNW(pszName, pbid->szTitle, cchName); } if (ppstb) { *ppstb = pbid->pdb; if (pbid->pdb) { pbid->pdb->AddRef(); } } if (pdwState) { *pdwState = 0; if (pbid->fShow) *pdwState = BSSF_VISIBLE; if (pbid->fNoTitle) *pdwState |= BSSF_NOTITLE; if (pbid->dwModeFlags & DBIMF_UNDELETEABLE) *pdwState |= BSSF_UNDELETEABLE; } pbid->Release(); return S_OK; } /*---------------------------------------------------------- Purpose: IBandSite::SetBandState * NOTES * failure handling is inconsistent (1 band vs. all bands case) */ HRESULT CBandSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState) { HRESULT hr; if (dwBandID == (DWORD) -1) { BOOL fChange = FALSE; for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { hr = _SetBandStateHelper(pbid->dwBandID, dwMask, dwState); ASSERT(SUCCEEDED(hr)); fChange |= (hr != S_OK); pbid->Release(); } else { return E_FAIL; } } if (fChange) _UpdateAllBands(FALSE, FALSE); return S_OK; } else { hr = _SetBandStateHelper(dwBandID, dwMask, dwState); if (SUCCEEDED(hr) && hr != S_OK) { _UpdateBand(dwBandID); return S_OK; } } return E_FAIL; } //*** // ENTRY/EXIT // ret S_OK|changed on success, o.w. E_*. // NOTES // only a helper for SetBandState, don't call directly HRESULT CBandSite::_SetBandStateHelper(DWORD dwBandID, DWORD dwMask, DWORD dwState) { HRESULT hr = E_FAIL; CBandItemData *pbid = _GetBandItem(_BandIDToIndex(dwBandID)); if (pbid) { DWORD dwOldState; if (FAILED(QueryBand(dwBandID, NULL, &dwOldState, NULL, 0))) { ASSERT(0); // 'impossible' dwOldState = (DWORD)-1; } if (dwMask & BSSF_VISIBLE) _ShowBand(pbid, dwState & BSSF_VISIBLE); if (dwMask & BSSF_NOTITLE) pbid->fNoTitle = BOOLIFY(dwState & BSSF_NOTITLE); // FEATURE: (kkahl): BSSF_UNDELETABLE cannot currently be modified with // this interface. hr = ResultFromShort((dwOldState ^ dwState) & dwMask); pbid->Release(); } return hr; } //*** _CheckNotifyOnAddRemove -- handle notifies for add/remove/empty // DESCRIPTION // add/remove always sends a BSID_BANDADDED/BSID_BANDREMOVED. // remove of last always sends a DBCID_EMPTY. // in floating mode, a transition to/from 1 band does a refresh. // void CBandSite::_CheckNotifyOnAddRemove(DWORD dwBandID, int iCode) { int cBands; if (!_pct) return; if (iCode == CNOAR_CLOSEBAR) { // Shut down the whole thing cBands = 0; } else { VARIANTARG var; int nCmdID; cBands = _GetBandItemCount(); // post-op # (since op happened in caller) VariantInit(&var); var.vt = VT_UI4; var.ulVal = dwBandID; BOOL fOne = FALSE; switch (iCode) { case CNOAR_ADDBAND: fOne = (cBands == 2); // 1->2 nCmdID = BSID_BANDADDED; break; case CNOAR_REMOVEBAND: fOne = (cBands == 1); // 2->1 nCmdID = BSID_BANDREMOVED; break; default: ASSERT(0); return; } if ((fOne && (_dwMode & DBIF_VIEWMODE_FLOATING))) { // n.b. fBSOnly *must* be TRUE for perf _UpdateAllBands(TRUE, TRUE); // force refresh of optional gripper/title } _pct->Exec(&CGID_BandSite, nCmdID, 0, &var, NULL); } if (cBands == 0) { ASSERT(iCode != CNOAR_ADDBAND); // sanity check _pct->Exec(&CGID_DeskBarClient, DBCID_EMPTY, 0, NULL, NULL); } return; } /*---------------------------------------------------------- Purpose: IBandSite::RemoveBand method */ HRESULT CBandSite::RemoveBand(DWORD dwBandID) { int iIndex = _BandIDToIndex(dwBandID); CBandItemData *pbid = _GetBandItem(iIndex); if (pbid) { // Release the banditem data first, while it can still // receive cleanup notifications from its control. *Then* // delete the band item. _ReleaseBandItemData(pbid, iIndex); _DeleteBandItem(iIndex); // unhook from host (rebar) _CheckNotifyOnAddRemove(dwBandID, CNOAR_REMOVEBAND); pbid->Release(); return S_OK; } return E_FAIL; } void CBandSite::_OnCloseBand(DWORD dwBandID) { if (dwBandID == -1) { // Close everything _CheckNotifyOnAddRemove(dwBandID, CNOAR_CLOSEBAR); } else { // Close just this band CBandItemData *pbid = _GetBandItemDataStructByID(dwBandID); if (pbid) { if (ConfirmRemoveBand(_hwnd, IDS_CONFIRMCLOSEBAND, pbid->szTitle)) { RemoveBand(dwBandID); } pbid->Release(); } } } void CBandSite::_MinimizeBand(DWORD dwBandID) { SendMessage(_hwnd, RB_MINIMIZEBAND, _BandIDToIndex(dwBandID), TRUE); } void CBandSite::_MaximizeBand(DWORD dwBandID) { SendMessage(_hwnd, RB_MAXIMIZEBAND, _BandIDToIndex(dwBandID), TRUE); } // // private insert a band into the container control by ID // returns the band ID in ShortFromResult(hres) // HRESULT CBandSite::_AddBandByID(IUnknown *punk, DWORD dwID) { IDeskBand *pdb; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb)); if (SUCCEEDED(hr)) { ASSERT(pdb); CBandItemData *pbid = new CBandItemData(); if (pbid) { pbid->dwBandID = dwID; pbid->pdb = pdb; pdb->AddRef(); pbid->fShow = TRUE; // initially visible pbid->pdb->QueryInterface(IID_PPV_ARG(IWinEventHandler, &pbid->pweh)); hr = IUnknown_SetSite(pbid->pdb, SAFECAST(this, IBandSite*)); if (SUCCEEDED(hr)) { hr = pbid->pdb->GetWindow(&pbid->hwnd); if (SUCCEEDED(hr)) { // takes ownership in success case if (_AddBandItem(pbid)) { if (_dwShowState == DBC_SHOW) { ASSERT(pbid->fShow); pbid->pdb->ShowDW(TRUE); _MinimizeBand(pbid->dwBandID); } _CheckNotifyOnAddRemove(pbid->dwBandID, CNOAR_ADDBAND); hr = ResultFromShort(pbid->dwBandID); // success } else { hr = E_FAIL; } } } if (FAILED(hr)) { // clean up _ReleaseBandItemData(pbid, -1); } // Now that we've added the band, clear the _SendToToolband cache. // // We need to do this because we might have gotten a message for // the band before it was inserted, in which case we'll have cached // a NULL handler for the band's hwnd (preventing the band from // getting any messages thereafter). ATOMICRELEASE(_pwehCache); _hwndCache = NULL; } else { hr = E_OUTOFMEMORY; } pdb->Release(); } return hr; } /*---------------------------------------------------------- Purpose: IBandSite::AddBand method. Insert a band into the container control. Returns: the band ID in ShortFromResult(hres) */ HRESULT CBandSite::AddBand(IUnknown *punk) { HRESULT hres = _AddBandByID(punk, _dwBandIDNext); if (SUCCEEDED(hres)) { _dwBandIDNext++; } return hres; } void CBandSite::_UpdateBand(DWORD dwBandID) { CBandItemData *pbid = _GetBandItem(_BandIDToIndex(dwBandID)); if (pbid) { _UpdateBandInfo(pbid, FALSE); _OnRBAutoSize(NULL); pbid->Release(); } } void CBandSite::_UpdateAllBands(BOOL fBSOnly, BOOL fNoAutoSize) { BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0); for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { _UpdateBandInfo(pbid, fBSOnly); pbid->Release(); } } SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0); if (!fNoAutoSize) { SendMessage(_hwnd, RB_SIZETORECT, 0, 0); _OnRBAutoSize(NULL); } } // *** IOleCommandTarget *** HRESULT CBandSite::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { HRESULT hres = OLECMDERR_E_UNKNOWNGROUP; if (pguidCmdGroup) { if (IsEqualIID(*pguidCmdGroup, IID_IDockingWindow)) { for (ULONG i=0 ; i= 0; iBand--) { CBandItemData *pbid = _GetBandItem(iBand); if (pbid) { HRESULT hrTemp = IUnknown_QueryStatus(pbid->pdb, pguidCmdGroup, 1, &rgCmds[i], pcmdtext); pbid->Release(); if (SUCCEEDED(hrTemp) && ((rgCmds[i].cmdf & OLECMDF_SUPPORTED) && !(rgCmds[i].cmdf & OLECMDF_ENABLED))) { break; } } } break; default: rgCmds[i].cmdf = 0; break; } } return S_OK; } else if (IsEqualIID(*pguidCmdGroup, CGID_Explorer)) { return IUnknown_QueryStatus(_ptbActive, pguidCmdGroup, cCmds, rgCmds, pcmdtext); } } // if we got here, we didn't handle it // forward it down return MayQSForward(_ptbActive, OCTD_DOWN, pguidCmdGroup, cCmds, rgCmds, pcmdtext); } int _QueryServiceCallback(CBandItemData *pbid, void *pv) { QSDATA* pqsd = (QSDATA*)pv; if (pbid->fShow) pqsd->hres = IUnknown_QueryService(pbid->pdb, *(pqsd->pguidService), *(pqsd->piid), pqsd->ppvObj); // stop if we found the service return SUCCEEDED(pqsd->hres) ? FALSE : TRUE; } typedef struct { HRESULT hres; const GUID *pguidCmdGroup; DWORD nCmdID; DWORD nCmdexecopt; VARIANTARG *pvarargIn; VARIANTARG *pvarargOut; } EXECDATA; int _ExecCallback(CBandItemData *pbid, void *pv) { EXECDATA* ped = (EXECDATA*)pv; ped->hres = IUnknown_Exec(pbid->pdb, ped->pguidCmdGroup, ped->nCmdID, ped->nCmdexecopt, ped->pvarargIn, ped->pvarargOut); return 1; } HRESULT CBandSite::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hres = OLECMDERR_E_UNKNOWNGROUP; HRESULT hresTmp; if (pguidCmdGroup == NULL) { /*NOTHING*/ ; } else if (IsEqualIID(*pguidCmdGroup, CGID_DeskBand)) { switch (nCmdID) { case DBID_BANDINFOCHANGED: if (!pvarargIn) _UpdateAllBands(FALSE, FALSE); else if (pvarargIn->vt == VT_I4) _UpdateBand(pvarargIn->lVal); hres = S_OK; // forward this up. if (_pct) { _pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); } goto Lret; case DBID_PUSHCHEVRON: if (pvarargIn && pvarargIn->vt == VT_I4) { int iIndex = _BandIDToIndex(nCmdexecopt); SendMessage(_hwnd, RB_PUSHCHEVRON, iIndex, pvarargIn->lVal); hres = S_OK; } goto Lret; case DBID_MAXIMIZEBAND: if (pvarargIn && pvarargIn->vt == VT_UI4) _MaximizeBand(pvarargIn->ulVal); hres = S_OK; goto Lret; #if 1 // { FEATURE: temporary until add cbs::Select() mfunc case DBID_SHOWONLY: { int iCount = _GetBandItemCount(); // pvaIn->punkVal: // punk hide everyone except me // 0 hide everyone // 1 show everyone // FEATURE: we should use pvaIn->lVal not punkVal since we're // allowing 0 & 1 !!! (and not doing addref/release) ASSERT(pvarargIn && pvarargIn->vt == VT_UNKNOWN); if (pvarargIn->punkVal == NULL || pvarargIn->punkVal == (IUnknown*)1) TraceMsg(TF_BANDDD, "cbs.e: (id=DBID_SHOWONLY, punk=%x)", pvarargIn->punkVal); // show myself, hide everyone else TraceMsg(TF_BANDDD, "cbs.Exec(DBID_SHOWONLY): n=%d", _GetBandItemCount()); // wait to show this band until we've hidden the others CBandItemData *pbidShow = NULL; // FEATURE: this (IUnknown*)1 is bogus! Also mentioned above. BOOL bShowAll = (pvarargIn->punkVal == (IUnknown*)1); for (int i = iCount - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { BOOL fShow = bShowAll || SHIsSameObject(pbid->pdb, pvarargIn->punkVal); if (!fShow || bShowAll) { _ShowBand(pbid, fShow); } else { pbidShow = pbid; pbidShow->AddRef(); } pbid->Release(); } } if (pbidShow) { _ShowBand(pbidShow, TRUE); // nash:37290 set focus to band on open if (_dwShowState == DBC_SHOW) IUnknown_UIActivateIO(pbidShow->pdb, TRUE, NULL); else ASSERT(0); pbidShow->Release(); } } break; #endif // } } } else if (IsEqualIID(*pguidCmdGroup, CGID_Explorer)) { return IUnknown_Exec(_ptbActive, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); } else if (IsEqualIID(*pguidCmdGroup, CGID_DeskBarClient)) { switch (nCmdID) { case DBCID_ONDRAG: if (pvarargIn->vt == VT_I4) { ASSERT(pvarargIn->lVal == 0 || pvarargIn->lVal == DRAG_MOVE); TraceMsg(DM_TRACE, "cbs.e: DBCID_ONDRAG i=%d", pvarargIn->lVal); _fDragging = pvarargIn->lVal; } break; case DBCID_GETBAR: // return IUnkown of my IDeskBar host if ((pvarargOut != NULL) && _pdb) { ::VariantInit(pvarargOut); V_VT(pvarargOut) = VT_UNKNOWN; V_UNKNOWN(pvarargOut) = _pdb; _pdb->AddRef(); hres = S_OK; goto Lret; } break; } } // if we got here, we didn't handle it // see if we should forward it down hresTmp = IsExecForward(pguidCmdGroup, nCmdID); if (SUCCEEDED(hresTmp) && HRESULT_CODE(hresTmp) > 0) { // down (singleton or broadcast) if (HRESULT_CODE(hresTmp) == OCTD_DOWN) { // down (singleton) hres = IUnknown_Exec(_ptbActive, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); } else { // down (broadcast) // n.b. hres is a bit weird: 'last one wins' // FEATURE: should we just return S_OK? ASSERT(HRESULT_CODE(hresTmp) == OCTD_DOWNBROADCAST); EXECDATA ctd = { hres, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut }; _BandItemEnumCallback(1, _ExecCallback, &ctd); hres = ctd.hres; } } Lret: return hres; } /*** _ShowBand -- show/hide band (cached state, band, and rebar band) */ void CBandSite::_ShowBand(CBandItemData *pbid, BOOL fShow) { int i; pbid->fShow = BOOLIFY(fShow); if (pbid->pdb) { pbid->pdb->ShowDW(fShow && (_dwShowState == DBC_SHOW)); } i = _BandIDToIndex(pbid->dwBandID); SendMessage(_hwnd, RB_SHOWBAND, i, fShow); // get me a window to draw D&D curosors on. . . SHGetTopBrowserWindow(SAFECAST(this, IBandSite*), &_hwndDD); } /*---------------------------------------------------------- Purpose: IBandSite::GetBandSiteInfo */ HRESULT CBandSite::GetBandSiteInfo(BANDSITEINFO * pbsinfo) { ASSERT(IS_VALID_WRITE_PTR(pbsinfo, BANDSITEINFO)); if (pbsinfo->dwMask & BSIM_STATE) pbsinfo->dwState = _dwMode; if (pbsinfo->dwMask & BSIM_STYLE) pbsinfo->dwStyle = _dwStyle; return S_OK; } /*---------------------------------------------------------- Purpose: IBandSite::SetBandSiteInfo */ HRESULT CBandSite::SetBandSiteInfo(const BANDSITEINFO * pbsinfo) { ASSERT(IS_VALID_READ_PTR(pbsinfo, BANDSITEINFO)); if (pbsinfo->dwMask & BSIM_STATE) _dwMode = pbsinfo->dwState; if (pbsinfo->dwMask & BSIM_STYLE) { // If the BSIS_SINGLECLICK style changed, change the rebar style if ( _hwnd && ((_dwStyle ^ pbsinfo->dwStyle) & BSIS_SINGLECLICK) ) SHSetWindowBits(_hwnd, GWL_STYLE, RBS_DBLCLKTOGGLE, (pbsinfo->dwStyle & BSIS_SINGLECLICK)?0:RBS_DBLCLKTOGGLE); _dwStyle = pbsinfo->dwStyle; } return S_OK; } /*---------------------------------------------------------- Purpose: IBandSite::GetBandObject */ HRESULT CBandSite::GetBandObject(DWORD dwBandID, REFIID riid, void **ppvObj) { HRESULT hres = E_FAIL; *ppvObj = NULL; if (IsEqualIID(riid, IID_IDataObject)) { *ppvObj = _DataObjForBand(dwBandID); if (*ppvObj) hres = S_OK; } else { CBandItemData *pbid = _GetBandItemDataStructByID(dwBandID); if (pbid) { if (pbid->pdb) { hres = pbid->pdb->QueryInterface(riid, ppvObj); } pbid->Release(); } } return hres; } /*---------------------------------------------------------- Purpose: Returns a pointer to the band item data given an externally known band ID. Returns: NULL if band ID is illegal */ CBandItemData* CBandSite::_GetBandItemDataStructByID(DWORD uID) { int iBand = _BandIDToIndex(uID); if (iBand == -1) return NULL; return _GetBandItem(iBand); } __inline HRESULT _FwdWinEvent(IWinEventHandler* pweh, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres) { ASSERT(pweh); ASSERT(hwnd == HWND_BROADCAST || pweh->IsWindowOwner(hwnd) == S_OK); return pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, plres); } /*---------------------------------------------------------- Purpose: Forwards messages to the band that owns the window. Returns: TRUE if the message was forwarded */ BOOL CBandSite::_SendToToolband(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres) { BOOL fSent = FALSE; LRESULT lres = 0; if (hwnd) { if (hwnd == _hwndCache) { ASSERT(hwnd != HWND_BROADCAST); if (_pwehCache) { _FwdWinEvent(_pwehCache, hwnd, uMsg, wParam, lParam, &lres); fSent = TRUE; } } else { CBandItemData *pbid = NULL; // pbid ownership is goofy here -- we still have a ref once we break out of the loop. for (int i = _GetBandItemCount() - 1; i >= 0; i--) { if (pbid) pbid->Release(); pbid = _GetBandItem(i); if (pbid) { if (pbid->pweh) { if (hwnd == HWND_BROADCAST || (pbid->pweh->IsWindowOwner(hwnd) == S_OK)) { _FwdWinEvent(pbid->pweh, hwnd, uMsg, wParam, lParam, &lres); fSent = TRUE; if (hwnd != HWND_BROADCAST) { break; } } } else { if (hwnd == HWND_BROADCAST && pbid->hwnd) { lres = SendMessage(pbid->hwnd, uMsg, wParam, lParam); fSent = TRUE; } } } } if (hwnd != HWND_BROADCAST) { ATOMICRELEASE(_pwehCache); _hwndCache = hwnd; if (fSent && pbid) { _pwehCache = pbid->pweh; _pwehCache->AddRef(); } } if (pbid) pbid->Release(); } } if (plres) *plres = lres; return fSent; } typedef struct { HWND hwnd; HRESULT hres; } WINDOWOWNERDATA; int _IsWindowOwnerCallback(CBandItemData *pbid, void *pv) { WINDOWOWNERDATA* pwod = (WINDOWOWNERDATA*)pv; if (pbid->pweh && (pbid->pweh->IsWindowOwner(pwod->hwnd) == S_OK)) { pwod->hres = S_OK; return 0; } return 1; } HRESULT CBandSite::IsWindowOwner(HWND hwnd) { if (hwnd == _hwnd) return S_OK; WINDOWOWNERDATA wod = { hwnd, S_FALSE }; _BandItemEnumCallback(1, _IsWindowOwnerCallback, &wod); return wod.hres; } //*** CBandSite::IDeskBarClient::* { HRESULT CBandSite::GetSize(DWORD dwWhich, LPRECT prc) { HRESULT hres = E_FAIL; switch (dwWhich) { case DBC_GS_IDEAL: { prc->right = 0; prc->bottom = 0; BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0); for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { RECT rc; SendMessage(_hwnd, RB_GETBANDBORDERS, _BandIDToIndex(pbid->dwBandID), (LPARAM) &rc); _UpdateBandInfo(pbid, FALSE); if (pbid->fShow) { if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) { prc->right = max(prc->right, pbid->ptActual.x + (rc.left + rc.right)); prc->bottom += pbid->ptActual.y + rc.top + rc.bottom; } else { prc->bottom = max(prc->right, pbid->ptActual.x + (rc.left + rc.right)); prc->right += pbid->ptActual.y + rc.top + rc.bottom; } } hres = S_OK; pbid->Release(); } } SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0); } break; case DBC_GS_SIZEDOWN: { // Used to make a band change size in chuncks SendMessage(_hwnd, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)prc); hres = S_OK; } break; } return hres; } void CBandSite::_Close() { if (_hwnd) { // (scotth): This method is getting called by the destructor, // and calls _DeleteAllBandItems, which sends messages to _hwnd. // _hwnd is already destroyed by this time. If you hit this assert // it is because in debug windows it RIPs like crazy. // 970508 (adp): pblm was that we weren't doing DestroyWnd etc. // // Do no remove this assert....please fix the root problem. ASSERT(IS_VALID_HANDLE(_hwnd, WND)); SendMessage(_hwnd, WM_SETREDRAW, 0, 0); _DeleteAllBandItems(); DestroyWindow(_hwnd); _hwnd = 0; } } HRESULT CBandSite::UIActivateDBC(DWORD dwState) { if (dwState != _dwShowState) { BOOL fShow = dwState; _dwShowState = dwState; // map UIActivateDBC to ShowDW if (DBC_SHOWOBSCURE == dwState) fShow = FALSE; BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0); for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { if (pbid->pdb) { pbid->pdb->ShowDW(fShow && pbid->fShow); } pbid->Release(); } } // do this now intead of at creation so that // rebar doesn't keep trying to autosize us while // we're not even visible SHSetWindowBits(_hwnd, GWL_STYLE, RBS_AUTOSIZE, RBS_AUTOSIZE); SendMessage(_hwnd, WM_SIZE, 0, 0); SendMessage(_hwnd, WM_SETREDRAW, (DBC_SHOW == dwState) ? TRUE : fRedraw, 0); } return S_OK; } DWORD CBandSite::_GetWindowStyle(DWORD* pdwExStyle) { *pdwExStyle = WS_EX_TOOLWINDOW; DWORD dwStyle = RBS_REGISTERDROP | RBS_VARHEIGHT | RBS_BANDBORDERS | WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN; if (_dwStyle & BSIS_LEFTALIGN) { dwStyle |= RBS_VERTICALGRIPPER; } if (!(_dwStyle & BSIS_SINGLECLICK)) { dwStyle |= RBS_DBLCLKTOGGLE; } return dwStyle; } HRESULT CBandSite::_Initialize(HWND hwndParent) { // // I hope we have an IBandSite to talk to. // if (!_pbsOuter) return E_FAIL; if (!_hwnd) { DWORD dwExStyle; DWORD dwStyle = _GetWindowStyle(&dwExStyle); _hwnd = CreateWindowEx(dwExStyle, REBARCLASSNAME, NULL, dwStyle, 0, 0, 0, 0, hwndParent, (HMENU) FCIDM_REBAR, HINST_THISDLL, NULL); if (_hwnd) { SendMessage(_hwnd, RB_SETTEXTCOLOR, 0, CLR_DEFAULT); SendMessage(_hwnd, RB_SETBKCOLOR, 0, CLR_DEFAULT); SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0); } } return _hwnd ? S_OK : E_OUTOFMEMORY; } HRESULT CBandSite::SetDeskBarSite(IUnknown* punkSite) { HRESULT hr = S_OK; if (!punkSite) { // Time to tell the bands to free their // back pointers on us or we never get freed... // 970325 for now bs::SetDeskBarSite(NULL) is 'overloaded' // to mean do both a CloseDW and a SetSite. // when we clean up our act and have a bs::Close iface // we'll go back to the '#else' code below. if (_hwnd) _Close(); } ATOMICRELEASE(_pct); ATOMICRELEASE(_pdb); ATOMICRELEASE(_punkSite); if (_pbp && _fCreatedBandProxy) _pbp->SetSite(punkSite); if (punkSite) { _punkSite = punkSite; _punkSite->AddRef(); if (!_hwnd) { HWND hwndParent; IUnknown_GetWindow(punkSite, &hwndParent); hr = _Initialize(hwndParent); } punkSite->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_pct)); punkSite->QueryInterface(IID_PPV_ARG(IDeskBar, &_pdb)); } return hr; } HRESULT CBandSite::SetModeDBC(DWORD dwMode) { if (dwMode != _dwMode) { _dwMode = dwMode; if (_hwnd) { DWORD dwStyle = 0; if (dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) { dwStyle |= CCS_VERT; } SHSetWindowBits(_hwnd, GWL_STYLE, CCS_VERT, dwStyle); } _UpdateAllBands(FALSE, FALSE); } return S_OK; } // } IDropTarget* CBandSite::_WrapDropTargetForBand(IDropTarget* pdtBand) { if (!pdtBand || (_dwStyle & BSIS_NODROPTARGET)) { // addref it for the new pointer if (pdtBand) pdtBand->AddRef(); return pdtBand; } else { return DropTargetWrap_CreateInstance(pdtBand, SAFECAST(this, IDropTarget*), _hwndDD); } } LRESULT CBandSite::_OnNotify(LPNMHDR pnm) { NMOBJECTNOTIFY *pnmon = (NMOBJECTNOTIFY *)pnm; switch (pnm->code) { case RBN_GETOBJECT: { pnmon->hResult = E_FAIL; // if we're the drag source, then a band is dragging... we want to only // give out the bandsite's drop target if (pnmon->iItem != -1 && !_fDragSource) { CBandItemData *pbid = _GetBandItemDataStructByID(pnmon->iItem); if (pbid) { if (pbid->pdb) { pnmon->hResult = pbid->pdb->QueryInterface(*pnmon->piid, &pnmon->pObject); // give a wrapped droptarget instead of the band's droptarget if (IsEqualIID(*pnmon->piid, IID_IDropTarget)) { IDropTarget* pdtBand; BOOL fNeedReleasePdtBand = FALSE; if (SUCCEEDED(pnmon->hResult)) { pdtBand = (IDropTarget*)(pnmon->pObject); } else { CDropDummy *pdtgt = new CDropDummy(_hwndDD); pdtBand = SAFECAST(pdtgt, IDropTarget*); fNeedReleasePdtBand = TRUE; } IDropTarget* pdt = _WrapDropTargetForBand(pdtBand); if (pdt) { pnmon->pObject = pdt; pnmon->hResult = S_OK; // we've handed off pdtBand to pdt fNeedReleasePdtBand = TRUE; } if (fNeedReleasePdtBand && pdtBand) pdtBand->Release(); } if (FAILED(pnmon->hResult) && !(_dwStyle & BSIS_NODROPTARGET)) pnmon->hResult = QueryInterface(*pnmon->piid, &pnmon->pObject); } pbid->Release(); } } break; } case RBN_BEGINDRAG: return _OnBeginDrag((NMREBAR*)pnm); case RBN_AUTOSIZE: _OnRBAutoSize((NMRBAUTOSIZE*)pnm); break; case RBN_CHEVRONPUSHED: { LPNMREBARCHEVRON pnmch = (LPNMREBARCHEVRON) pnm; CBandItemData *pbid = _GetBandItem(pnmch->uBand); if (pbid) { MapWindowPoints(_hwnd, HWND_DESKTOP, (LPPOINT)&pnmch->rc, 2); ToolbarMenu_Popup(_hwnd, &pnmch->rc, pbid->pdb, pbid->hwnd, 0, (DWORD)pnmch->lParamNM); pbid->Release(); } break; } case RBN_AUTOBREAK: { if (_dwStyle & BSIS_PREFERNOLINEBREAK) { Comctl32_FixAutoBreak(pnm); } break; } } return 0; } void CBandSite::_OnRBAutoSize(NMRBAUTOSIZE* pnm) { // DRAG_MOVE: we turn off autosize during (most of) a move because // fVertical is out of sync until the very end if (_pdb && _GetBandItemCount() && _fDragging != DRAG_MOVE) { RECT rc; int iHeightCur; int iHeight = (int)SendMessage(_hwnd, RB_GETBARHEIGHT, 0, 0); #ifdef DEBUG DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE); #endif GetWindowRect(_hwnd, &rc); MapWindowRect(HWND_DESKTOP, GetParent(_hwnd), &rc); if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) { ASSERT((dwStyle & CCS_VERT)); iHeightCur = RECTWIDTH(rc); rc.right = rc.left + iHeight; } else { ASSERT(!(dwStyle & CCS_VERT)); iHeightCur = RECTHEIGHT(rc); rc.bottom = rc.top + iHeight; } if ((iHeightCur != iHeight) || (IsOS(OS_WHISTLERORGREATER))) { _pdb->OnPosRectChangeDB(&rc); } } } IDataObject* CBandSite::_DataObjForBand(DWORD dwBandID) { IDataObject* pdtobjReturn = NULL; CBandItemData *pbid = _GetBandItemDataStructByID(dwBandID); if (pbid) { if (pbid->pdb) { CBandDataObject* pdtobj = new CBandDataObject(); if (pdtobj) { if (SUCCEEDED(pdtobj->Init(pbid->pdb, this, dwBandID))) { pdtobjReturn = pdtobj; pdtobjReturn->AddRef(); } pdtobj->Release(); } } pbid->Release(); } return pdtobjReturn; } LRESULT CBandSite::_OnBeginDrag(NMREBAR* pnm) { LRESULT lres = 0; DWORD dwBandID = _IndexToBandID(pnm->uBand); IDataObject* pdtobj = _DataObjForBand(dwBandID); ATOMICRELEASE(_pdtobj); _uDragBand = pnm->uBand; _pdtobj = pdtobj; // because the RBN_BEGINDRAG is synchronous and so is SHDoDragDrop // post this message to ourselves instead of calling dragdrop directly. // note that we don't have a window of our own, so we post to our parent // and let the message reflector send it back to us PostMessage(GetParent(_hwnd), WM_COMMAND, MAKELONG(0, IDM_DRAGDROP), (LPARAM)_hwnd); return 1; } // return TRUE if the user drags out of the rect of the rebar meaning that we should // go into ole drag drop. BOOL CBandSite::_PreDragDrop() { BOOL f = FALSE; RECT rc; POINT pt; DWORD dwBandID = _IndexToBandID(_uDragBand); // Find the BandID before an reordering that may happen. GetWindowRect(_hwnd, &rc); SetCapture(_hwnd); InflateRect(&rc, GetSystemMetrics(SM_CXEDGE) * 3, GetSystemMetrics(SM_CYEDGE) * 3); while (GetCapture() == _hwnd) { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { switch (msg.message) { case WM_MOUSEMOVE: GetCursorPos(&pt); if (!ISMOVEDDISABLED(dwBandID)) { if (PtInRect(&rc, pt)) { SendMessage(_hwnd, RB_DRAGMOVE, 0, (LPARAM)-1); } else if (!ISDDCLOSEDISABLED(dwBandID) && _pdtobj) { // we've moved out of the bounds of the rebar.. switch to ole drag f = TRUE; SetCapture(NULL); } } break; case WM_LBUTTONUP: case WM_LBUTTONDOWN: case WM_MBUTTONUP: case WM_MBUTTONDOWN: case WM_RBUTTONUP: case WM_RBUTTONDOWN: // bail on any mouse button action SetCapture(NULL); break; case WM_KEYDOWN: switch (msg.wParam) { case VK_ESCAPE: SetCapture(NULL); break; } // fall through default: TranslateMessage(&msg); DispatchMessage(&msg); } } } if (ISDDCLOSEDISABLED(dwBandID) || !_IsBandDeleteable(dwBandID)) { /// if don't allow close, never return true for ole drag. f = FALSE; } return f; } void CBandSite::_DoDragDrop() { DWORD dwBandID = _IndexToBandID(_uDragBand); DWORD dwEffect = DROPEFFECT_MOVE; _fDragSource = TRUE; SendMessage(_hwnd, RB_BEGINDRAG, _uDragBand, (LPARAM)-2); HRESULT hres = S_OK; // first check to see if we even need to go into Ole drag, or if // it can all be contained within the rebar if (_PreDragDrop()) { SHLoadOLE(SHELLNOTIFY_OLELOADED); // Browser Only - our shell32 doesn't know ole has been loaded hres = SHDoDragDrop(_hwnd, _pdtobj, NULL, dwEffect, &dwEffect); } else { // if we kept it all within win32 dragging, then set no drop effect dwEffect = DROPEFFECT_NONE; } SendMessage(_hwnd, RB_ENDDRAG, 0, 0); _fDragSource = FALSE; if (dwEffect & DROPEFFECT_MOVE) { RemoveBand(dwBandID); } else if (!dwEffect && hres == DRAGDROP_S_DROP) { // if the drop was done, but the target didn't allow // then we float the band. } ATOMICRELEASE(_pdtobj); } HMENU CBandSite::_LoadContextMenu() { return LoadMenuPopup_PrivateNoMungeW(MENU_BANDSITE1); } HRESULT CBandSite::_OnBSCommand(int idCmd, DWORD idBandActive, CBandItemData *pbid) { HRESULT hr = S_OK; switch (idCmd) { case BSIDM_CLOSEBAND: _OnCloseBand(idBandActive); break; case BSIDM_SHOWTITLEBAND: ASSERT(idBandActive != (DWORD)-1 && pbid); if (pbid) { pbid->fNoTitle = !pbid->fNoTitle; _UpdateBandInfo(pbid, FALSE); } break; case BSIDM_IEAK_DISABLE_MOVE: case BSIDM_IEAK_DISABLE_DDCLOSE: ASSERT(idBandActive != (DWORD)-1); if (idBandActive != (DWORD)-1) { static const int idCmds[] = { BSIDM_IEAK_DISABLE_MOVE, BSIDM_IEAK_DISABLE_DDCLOSE }; static const int idFlags[] = { BAND_ADMIN_NOMOVE, BAND_ADMIN_NODDCLOSE }; DWORD dwFlag = SHSearchMapInt(idCmds, idFlags, ARRAYSIZE(idCmds), idCmd); DWORD dwAdminSettings = _GetAdminSettings(idBandActive); // Toggle Setting. ToggleFlag(dwAdminSettings, dwFlag); // Set Menu Item Check Mark appropriately. _SetAdminSettings(idBandActive, dwAdminSettings); } break; default: ASSERT(0); hr = E_FAIL; break; } return hr; } // returns the index of the band hit by lParam using context menu semantics (lParam == -1 for keyboard) int CBandSite::_ContextMenuHittest(LPARAM lParam, POINT* ppt) { int iBandIndex; if (lParam == (LPARAM) -1) { // Keyboard activation. Use active band. DWORD dwBandID = _BandIDFromPunk(_ptbActive); iBandIndex = _BandIDToIndex(dwBandID); CBandItemData *pbid = _GetBandItem(iBandIndex); if (pbid) { RECT rc; GetWindowRect(pbid->hwnd, &rc); ppt->x = rc.left; ppt->y = rc.top; pbid->Release(); } } else { // Mouse activation. Figure out which band got clicked. RBHITTESTINFO rbht; ppt->x = GET_X_LPARAM(lParam); ppt->y = GET_Y_LPARAM(lParam); rbht.pt = *ppt; ScreenToClient(_hwnd, &rbht.pt); SendMessage(_hwnd, RB_HITTEST, 0, (LPARAM)&rbht); iBandIndex = rbht.iBand; } return iBandIndex; } HRESULT CBandSite::_OnContextMenu(WPARAM wParam, LPARAM lParam) { HRESULT hres = S_OK; HMENU hmenu = CreatePopupMenu(); if (hmenu) { HRESULT hresT; int idCmd = 1; IContextMenu *pcm, *pcmParent = NULL, *pcmChild = NULL; POINT pt; int iBandIndex = _ContextMenuHittest(lParam, &pt); // map rebar index to band id // get band info for that band id DWORD idBandActive = _IndexToBandID(iBandIndex); CBandItemData *pbid = _GetBandItemDataStructByID(idBandActive); // // self (top) // int idCmdBS1 = idCmd; HMENU hmenuMe = _LoadContextMenu(); if (hmenuMe) { BOOL fDeleteShowTitle = TRUE; if (pbid && !(_dwStyle & BSIS_LOCKED)) { DESKBANDINFO dbi; CheckMenuItem(hmenuMe, BSIDM_SHOWTITLEBAND, pbid->fNoTitle ? MF_BYCOMMAND|MF_UNCHECKED : MF_BYCOMMAND|MF_CHECKED); dbi.dwMask = 0; // paranoia (and needed for taskband!) _GetBandInfo(pbid, &dbi); // make sure pbid in sync ASSERT((dbi.dwMask & DBIM_TITLE) || pbid->fNoTitle); if ((dbi.dwMask & DBIM_TITLE) && _IsEnableTitle(pbid)) { fDeleteShowTitle = FALSE; } } if (fDeleteShowTitle) { DeleteMenu(hmenuMe, BSIDM_SHOWTITLEBAND, MF_BYCOMMAND); } idCmd += Shell_MergeMenus(hmenu, hmenuMe, 0, idCmd, 0x7fff, 0) - (idCmd); DestroyMenu(hmenuMe); } // // child // int idCmdChild = idCmd; if (pbid && pbid->pdb) { // merge in band's menu (at front) hresT = pbid->pdb->QueryInterface(IID_PPV_ARG(IContextMenu, &pcmChild)); if (SUCCEEDED(hresT)) { // 0=at front hresT = pcmChild->QueryContextMenu(hmenu, 0, idCmd, 0x7fff, 0); if (SUCCEEDED(hresT)) idCmd += HRESULT_CODE(hresT); } } // // self (bottom) // int idCmdBS2 = idCmd; if (!(_dwStyle & BSIS_NOCONTEXTMENU)) { hmenuMe = LoadMenuPopup_PrivateNoMungeW(MENU_BANDSITE2); if (hmenuMe) { // disable 'Close Band' if it's marked undeleteable // nash:17821: don't disable when 0 bands (so user can easily // get out of toasted mode) if ((idBandActive == (DWORD)-1) || // if mouse not over a band, delete close menu item (!_IsBandDeleteable(idBandActive) || ISDDCLOSEDISABLED(idBandActive)) || (_dwStyle & BSIS_LOCKED)) { DeleteMenu(hmenuMe, BSIDM_CLOSEBAND, MF_BYCOMMAND); } if (!_fIEAKInstalled) { DeleteMenu(hmenuMe, BSIDM_IEAK_DISABLE_DDCLOSE, MF_BYCOMMAND); DeleteMenu(hmenuMe, BSIDM_IEAK_DISABLE_MOVE, MF_BYCOMMAND); } else { DWORD dwAdminSettings = _GetAdminSettings(idBandActive); if (IsFlagSet(dwAdminSettings, BAND_ADMIN_NODDCLOSE)) _CheckMenuItem(hmenuMe, BSIDM_IEAK_DISABLE_DDCLOSE, TRUE); if (IsFlagSet(dwAdminSettings, BAND_ADMIN_NOMOVE)) _CheckMenuItem(hmenuMe, BSIDM_IEAK_DISABLE_MOVE, TRUE); } idCmd += Shell_MergeMenus(hmenu, hmenuMe, (UINT) -1, idCmd, 0x7fff, 0) - (idCmd); DestroyMenu(hmenuMe); } } // // parent // int idCmdParent = idCmd; if (_punkSite) { UINT uFlags = 0; ASSERT(_pcm3Parent == NULL); if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IContextMenu3, &_pcm3Parent)))) { uFlags |= CMF_ICM3; } hresT = _punkSite->QueryInterface(IID_PPV_ARG(IContextMenu, &pcmParent)); if (SUCCEEDED(hresT)) { // APPCOMPAT: fix parents and kids to handle... // we'd like to pass in -1 but not everyone handles that. // workaround: use _FixMenuIndex... hresT = pcmParent->QueryContextMenu(hmenu, _FixMenuIndex(hmenu, -1), idCmd, 0x7fff, uFlags); ASSERT(SUCCEEDED(hresT)); idCmd += HRESULT_CODE(hresT); } } // // do it // { HWND hwndParent = GetParent(_hwnd); if (!hwndParent) hwndParent = _hwnd; idCmd = TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, hwndParent, NULL); } if (idCmd) { // must test from smallest to largest ASSERT(idCmdBS1 <= idCmdChild); ASSERT(idCmdChild <= idCmdBS2); // o.w. test in wrong order ASSERT(idCmdBS2 <= idCmdParent); if ((idCmd>= idCmdBS1) && (idCmd < idCmdChild)) { idCmd -= idCmdBS1; hres = _OnBSCommand(idCmd, idBandActive, pbid); } else if ((idCmd>= idCmdBS2) && (idCmd < idCmdParent)) { idCmd -= idCmdBS2; hres = _OnBSCommand(idCmd, idBandActive, pbid); } else { // A parent or child command if (idCmd < idCmdParent) { pcm = pcmChild; idCmd -= idCmdChild; } else { pcm = pcmParent; idCmd -= idCmdParent; } ASSERT(pcm); // // Call InvokeCommand // CMINVOKECOMMANDINFOEX ici = { sizeof(CMINVOKECOMMANDINFOEX), 0L, _hwnd, (LPSTR)MAKEINTRESOURCE(idCmd), NULL, NULL, SW_NORMAL, }; hres = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); } } if (pbid) pbid->Release(); ATOMICRELEASE(_pcm3Parent); if (pcmParent) pcmParent->Release(); if (pcmChild) pcmChild->Release(); DestroyMenu(hmenu); } return hres; } /*---------------------------------------------------------- Purpose: IWinEventHandler::OnWinEvent Processes messages passed on from the bar. Forward messages to the bands as appropriate. */ HRESULT CBandSite::OnWinEvent(HWND h, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { HRESULT hres = E_FAIL; HWND hwnd = HWND_BROADCAST; switch (uMsg) { case WM_WININICHANGE: _UpdateAllBands(FALSE, FALSE); goto L_WM_SYSCOLORCHANGE; case WM_SYSCOLORCHANGE: case WM_PALETTECHANGED: L_WM_SYSCOLORCHANGE: // propagate to rebar if (_hwnd) SendMessage(_hwnd, uMsg, wParam, lParam); // by not returning here, it will get forwarded to the bands also... break; case WM_CONTEXTMENU: // if it came from the keyboard, wParam is somewhat useless. it's always out hwnd if (IS_WM_CONTEXTMENU_KEYBOARD(lParam)) hwnd = GetFocus(); else hwnd = (HWND)wParam; break; case WM_COMMAND: hwnd = GET_WM_COMMAND_HWND(wParam, lParam); break; case WM_NOTIFY: if (lParam) hwnd = ((LPNMHDR)lParam)->hwndFrom; break; case WM_INITMENUPOPUP: case WM_MEASUREITEM: case WM_DRAWITEM: case WM_MENUCHAR: if (_pcm3Parent) { // // If _pcm3Parent, then we've got a context menu up and // an ICM3 client who might care about this message. // hwnd = _hwnd; } break; default: return E_FAIL; } LRESULT lres = 0; if (hwnd) { if (_hwnd == hwnd) { // a message for us switch (uMsg) { case WM_NOTIFY: lres = _OnNotify((LPNMHDR)lParam); hres = S_OK; break; case WM_COMMAND: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case IDM_DRAGDROP: _DoDragDrop(); break; } break; case WM_INITMENUPOPUP: case WM_MEASUREITEM: case WM_DRAWITEM: case WM_MENUCHAR: ASSERT(_pcm3Parent); hres = _pcm3Parent->HandleMenuMsg2(uMsg, wParam, lParam, &lres); break; } } else { if (_SendToToolband(hwnd, uMsg, wParam, lParam, &lres)) hres = S_OK; } } switch (uMsg) { case WM_WININICHANGE: SendMessage(_hwnd, WM_SIZE, 0, 0); break; case WM_CONTEXTMENU: if (!lres) return _OnContextMenu(wParam, lParam); break; } if (plres) *plres = lres; return hres; } HRESULT CBandSite_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CBandSite *pbs = new CBandSite(pUnkOuter); if (pbs) { *ppunk = pbs->_GetInner(); return S_OK; } *ppunk = NULL; return E_OUTOFMEMORY; } //*** CBandSite::IPersistStream*::* { // HRESULT CBandSite::GetClassID(CLSID *pClassID) { *pClassID = CLSID_RebarBandSite; return S_OK; } HRESULT CBandSite::IsDirty(void) { ASSERT(0); return S_FALSE; // FEATURE: never be dirty? } HRESULT CBandSite::_AddBand(IUnknown* punk) { if (_pbsOuter) { // Give the outer guy first crack return _pbsOuter->AddBand(punk); } else { return AddBand(punk); } } // // Persisted CBandSite, use types that have fixes sizes // struct SBandSite { DWORD cbSize; DWORD cbVersion; DWORD cBands; // ...followed by length-preceded bands }; #define SBS_WOADMIN_VERSION 3 // Before we added admin settings. #define SBS_VERSION 8 //*** CBandSite::Load, Save -- // DESCRIPTION // for each band... // Load Read (i); OLFS(obj)+AddBand; Read (rbbi); RB_SBI // Save RB_GBI; Write(i); OSTS(obj)+nil ; Write(rbbi) // NOTES // FEATURE: needs error recovery // WARNING: we might have done a CreateBand w/o an AddBand; if so our // assumption about the rebar bands and the iunknowns being 'parallel' // is bogus. HRESULT CBandSite::Load(IStream *pstm) { HRESULT hres; SBandSite sfoo; hres = IStream_Read(pstm, &sfoo, sizeof(sfoo)); // pstm->Read if (hres == S_OK) { if (!(sfoo.cbSize == sizeof(SBandSite) && (sfoo.cbVersion == SBS_VERSION || sfoo.cbVersion == SBS_WOADMIN_VERSION))) { hres = E_FAIL; } IBandSiteHelper *pbsh; hres = QueryInterface(IID_PPV_ARG(IBandSiteHelper, &pbsh)); // QI self for aggregation stuff? if (SUCCEEDED(hres)) { BOOL_PTR fRedraw = SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0); for (DWORD i = 0; i < sfoo.cBands && SUCCEEDED(hres); ++i) { DWORD j; hres = IStream_Read(pstm, &j, sizeof(j)); // pstm->Read if (hres == S_OK) { if (j == i) // for sanity check { IUnknown* punk; hres = pbsh->LoadFromStreamBS(pstm, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hres)) { hres = _AddBand(punk); if (SUCCEEDED(hres)) { hres = _LoadBandInfo(pstm, i, sfoo.cbVersion); } punk->Release(); } } else { hres = E_FAIL; } } } SendMessage(_hwnd, WM_SETREDRAW, fRedraw, 0); pbsh->Release(); } _UpdateAllBands(FALSE, TRUE); // force refresh } return hres; } HRESULT CBandSite::Save(IStream *pstm, BOOL fClearDirty) { HRESULT hres; SBandSite sfoo; TraceMsg(DM_PERSIST, "cbs.s enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm)); sfoo.cbSize = sizeof(SBandSite); sfoo.cbVersion = SBS_VERSION; sfoo.cBands = _GetBandItemCount(); TraceMsg(DM_PERSIST, "cdb.s: cbands=%d", sfoo.cBands); hres = pstm->Write(&sfoo, sizeof(sfoo), NULL); if (SUCCEEDED(hres)) { for (DWORD i = 0; i < sfoo.cBands; i++) { // FEATURE: put seek ptr so can resync after bogus streams hres = pstm->Write(&i, sizeof(i), NULL); // for sanity check if (SUCCEEDED(hres)) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { if (pbid->pdb) { IBandSiteHelper *pbsh; hres = QueryInterface(IID_PPV_ARG(IBandSiteHelper, &pbsh)); if (SUCCEEDED(hres)) { hres = pbsh->SaveToStreamBS(SAFECAST(pbid->pdb, IUnknown*), pstm); pbsh->Release(); } } pbid->Release(); } hres = _SaveBandInfo(pstm, i); ASSERT(SUCCEEDED(hres)); } } } TraceMsg(DM_PERSIST, "cbs.s leave tell()=%x", DbStreamTell(pstm)); return hres; } HRESULT CBandSite::GetSizeMax(ULARGE_INTEGER *pcbSize) { // this is supposed to be an UPPER bound but we're returning a lower bound... static const ULARGE_INTEGER cbMax = { sizeof(SBandSite), 0 }; *pcbSize = cbMax; return S_OK; } BOOL CBandSite::_IsHeightReasonable(UINT cy) { static UINT s_cyMon = 0; if (s_cyMon == 0) { HMONITOR hmon = MonitorFromWindow(_hwnd, MONITOR_DEFAULTTONEAREST); if (hmon) { RECT rc; if (GetMonitorRect(hmon, &rc)) { s_cyMon = RECTHEIGHT(rc); } } } return (s_cyMon != 0) ? (cy < 4 * s_cyMon) : TRUE; } // returns: IStream::Read() semantics, S_OK means complete read HRESULT CBandSite::_LoadBandInfo(IStream *pstm, int i, DWORD dwVersion) { PERSISTBANDINFO bi; HRESULT hres; DWORD dwSize = sizeof(bi); bi.dwAdminSettings = BAND_ADMIN_NORMAL; // Assume Normal since it's not specified COMPILETIME_ASSERT(sizeof(PERSISTBANDINFO_V3) <= sizeof(PERSISTBANDINFO)); if (SBS_WOADMIN_VERSION == dwVersion) dwSize = sizeof(PERSISTBANDINFO_V3); hres = IStream_Read(pstm, &bi, dwSize); // pstm->Read if (hres == S_OK) { // // Sanity-check the height specified by PERSISTBANDINFO before proceeding. // Some people are hitting a stress scenario where a bad height gets // persisted out. If the height is not reasonable, then just discard // the sizing values (leaving the defaults in place). // if (_IsHeightReasonable(bi.cyChild)) { REBARBANDINFO rbbi = { 0 }; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_XPERSIST; rbbi.cx = bi.cx; rbbi.fStyle = bi.fStyle; // these things can change from instantiation to instantiation. // we want to restore the visual state, not the sizing rules. // the sizing rules re retreived each time in getbandinfo rbbi.cyMinChild = -1; rbbi.cyMaxChild = -1; rbbi.cyIntegral = -1; rbbi.cxMinChild = -1; if (rbbi.fStyle & RBBS_VARIABLEHEIGHT) { rbbi.cyChild = bi.cyChild; } else { rbbi.cyMinChild = bi.cyMinChild; } SendMessage(_hwnd, RB_SETBANDINFO, i, (LPARAM) &rbbi); } CBandItemData *pbid = _GetBandItem(i); if (pbid) { pbid->dwAdminSettings = bi.dwAdminSettings; pbid->fNoTitle = bi.fNoTitle; pbid->Release(); } } return hres; } HRESULT CBandSite::_SaveBandInfo(IStream *pstm, int i) { REBARBANDINFO rbbi = {0}; PERSISTBANDINFO bi = {0}; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_XPERSIST; SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM) &rbbi); ASSERT((rbbi.fMask & RBBIM_XPERSIST) == RBBIM_XPERSIST); bi.cx = rbbi.cx; bi.fStyle = rbbi.fStyle; bi.cyMinChild = rbbi.cyMinChild; bi.cyChild = rbbi.cyChild; CBandItemData *pbid = _GetBandItem(i); if (pbid) { bi.dwAdminSettings = pbid->dwAdminSettings; bi.fNoTitle = pbid->fNoTitle; pbid->Release(); } return pstm->Write(&bi, sizeof(bi), NULL); } void CBandSite::_CacheActiveBand(IUnknown *ptb) { if (ptb == _ptbActive) return; if (SHIsSameObject(ptb, _ptbActive)) return; ATOMICRELEASE(_ptbActive); if (ptb != NULL) { #ifdef DEBUG // better be an IInputObject or else why did you call us? IInputObject *pio; if (EVAL(SUCCEEDED(ptb->QueryInterface(IID_PPV_ARG(IInputObject, &pio))))) pio->Release(); // overly strict, but in our case it's true... IDeskBand *pdb; if (EVAL(SUCCEEDED(ptb->QueryInterface(IID_PPV_ARG(IDeskBand, &pdb))))) pdb->Release(); #endif _ptbActive = ptb; _ptbActive->AddRef(); } return; } DWORD CBandSite::_BandIDFromPunk(IUnknown* punk) { DWORD dwBandID = -1; DWORD dwBandIDTest; int cBands = EnumBands(-1, NULL); IUnknown* punkTest; if (punk) { for (int i = 0; i < cBands; i++) { if (SUCCEEDED(EnumBands(i, &dwBandIDTest))) { if (SUCCEEDED(GetBandObject(dwBandIDTest, IID_PPV_ARG(IUnknown, &punkTest)))) { BOOL fEq = SHIsSameObject(punk, punkTest); punkTest->Release(); if (fEq) { dwBandID = dwBandIDTest; break; } } } } } return dwBandID; } //*** IInputObjectSite methods *** HRESULT CBandSite::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus) { if (_ptbActive) { if (!SHIsSameObject(_ptbActive, punk)) { // Deactivate current band since the current band is // not the caller TraceMsg(TF_ACCESSIBILITY, "CBandSite::OnFocusChangeIS (hwnd=0x%08X) deactivate band", _hwnd); UIActivateIO(FALSE, NULL); } } if (fSetFocus) _CacheActiveBand(punk); return IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), fSetFocus); } //*** IInputObject methods *** HRESULT CBandSite::UIActivateIO(BOOL fActivate, LPMSG lpMsg) { HRESULT hres = E_FAIL; TraceMsg(TF_ACCESSIBILITY, "CBandSite::UIActivateIO (hwnd=0x%08X) fActivate=%d", _hwnd, fActivate); ASSERT(NULL == lpMsg || IS_VALID_WRITE_PTR(lpMsg, MSG)); if (_ptbActive) { hres = IUnknown_UIActivateIO(_ptbActive, fActivate, lpMsg); } else { hres = OnFocusChangeIS(NULL, fActivate); } if (fActivate) { if (!_ptbActive) { if (IsVK_TABCycler(lpMsg)) hres = _CycleFocusBS(lpMsg); else hres = S_OK; } } else { _CacheActiveBand(NULL); } return hres; } HRESULT CBandSite::HasFocusIO() { // Rebar should never get focus // NT #288832 Is one case where (GetFocus() == _hwnd) // which is caused when the "Folder Bar" disappears. // CExplorerBand::ShowDW() calls ShowWindow(hwndTreeView, SW_HIDE) // which by default sets focus to the parent (us). // This is ok because when this function is called, // it will return E_FAIL which the caller will treat // as S_FALSE and give the focus to the next deserving // dude in line. return IUnknown_HasFocusIO(_ptbActive); } HRESULT CBandSite::TranslateAcceleratorIO(LPMSG lpMsg) { TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) key=%d", _hwnd, lpMsg->wParam); if (IUnknown_TranslateAcceleratorIO(_ptbActive, lpMsg) == S_OK) { TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) key=%d; handled by active band", _hwnd, lpMsg->wParam); // active band handled it return S_OK; } else if (IsVK_TABCycler(lpMsg)) { TraceMsg(TF_ACCESSIBILITY, "CBandSite::TranslateAcceleratorIO (hwnd=0x%08X) cycle focus", _hwnd); // it's a tab; cycle focus return _CycleFocusBS(lpMsg); } return S_FALSE; } int CBandSite::_BandIndexFromPunk(IUnknown *punk) { for (int i = 0; i < _GetBandItemCount(); i++) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { BOOL fSame = SHIsSameObject(pbid->pdb, punk); pbid->Release(); if (fSame) { return i; } } } return -1; } BOOL CBandSite::_IsBandTabstop(CBandItemData *pbid) { // A band is a tabstop if it is visible and has WS_TABSTOP if (pbid->fShow && pbid->hwnd && IsWindowVisible(pbid->hwnd)) { if (WS_TABSTOP & GetWindowStyle(pbid->hwnd)) return TRUE; } return FALSE; } #define INCDEC(i, fDec) (fDec ? i - 1 : i + 1) IUnknown* CBandSite::_GetNextTabstopBand(IUnknown* ptb, BOOL fBackwards) { // Find the first tabstop candidate int iBandCount = _GetBandItemCount(); int iBand = _BandIndexFromPunk(ptb); if (iBand == -1) { // Start at the end/beginning if (fBackwards) iBand = iBandCount - 1; else iBand = 0; } else { // Start one off the current band iBand = INCDEC(iBand, fBackwards); } IUnknown *punkRet = NULL; BOOL fDone = FALSE; // Loop til we find a tabstop band or we run off the end while (!fDone && 0 <= iBand && iBand < iBandCount) { CBandItemData *pbid = _GetBandItem(iBand); if (pbid) { if (_IsBandTabstop(pbid)) { punkRet = pbid->pdb; fDone = TRUE; } pbid->Release(); } // Try the next band iBand = INCDEC(iBand, fBackwards); } return punkRet; } HRESULT CBandSite::_CycleFocusBS(LPMSG lpMsg) { HRESULT hr = S_FALSE; IUnknown* ptbSave = NULL; if (_ptbActive) { // Save off the active band in ptbSave ptbSave = _ptbActive; ptbSave->AddRef(); // Deactivate active band and clear cache IUnknown_UIActivateIO(_ptbActive, FALSE, NULL); _CacheActiveBand(NULL); } if (ptbSave && IsVK_CtlTABCycler(lpMsg)) { // If ctl-tab and a band was active, then reject focus ASSERT(hr == S_FALSE); } else { BOOL fShift = (GetKeyState(VK_SHIFT) < 0); // Loop til we find a tabstop and successfully activate it // or til we run out of bands. // FEATURE: todo -- call SetFocus if UIActivateIO fails? IUnknown* ptbNext = ptbSave; while (ptbNext = _GetNextTabstopBand(ptbNext, fShift)) { if (IUnknown_UIActivateIO(ptbNext, TRUE, lpMsg) == S_OK) { hr = S_OK; break; } } } ATOMICRELEASE(ptbSave); return hr; } //*** CBandSite::IBandSiteHelper::* { // stuff to make it possible to overload the OleLoad/Save stuff so the // taskbar band does not have to be CoCreat able. kinda a hack... HRESULT CBandSite::LoadFromStreamBS(IStream *pstm, REFIID riid, void **ppv) { return OleLoadFromStream(pstm, riid, ppv); } HRESULT CBandSite::SaveToStreamBS(IUnknown *punk, IStream *pstm) { IPersistStream *ppstm; HRESULT hres = punk->QueryInterface(IID_PPV_ARG(IPersistStream, &ppstm)); if (SUCCEEDED(hres)) { hres = OleSaveToStream(ppstm, pstm); ppstm->Release(); } return hres; } // } // *** IDropTarget *** { HRESULT CBandSite::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { TraceMsg(TF_BANDDD, "CBandSite::DragEnter %d %d", pt.x, pt.y); if (!_fDragSource) { FORMATETC fmte = {g_cfDeskBand, NULL, 0, -1, TYMED_ISTREAM}; _dwDropEffect = DROPEFFECT_NONE; if (pdtobj->QueryGetData(&fmte) == S_OK) { _dwDropEffect = DROPEFFECT_MOVE; } else { LPITEMIDLIST pidl; if (SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0))) { ASSERT(pidl && IS_VALID_PIDL(pidl)); DWORD dwAttrib = SFGAO_FOLDER | SFGAO_BROWSABLE; IEGetAttributesOf(pidl, &dwAttrib); ILFree(pidl); DWORD dwRAction; if (FAILED(IUnknown_HandleIRestrict(_punkSite, &RID_RDeskBars, RA_DROP, NULL, &dwRAction))) dwRAction = RR_ALLOW; if (dwRAction == RR_DISALLOW) _dwDropEffect = DROPEFFECT_NONE; else { // if it's not a folder nor a browseable object, we can't host it. if ((dwAttrib & SFGAO_FOLDER) || (dwAttrib & SFGAO_BROWSABLE) && (grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT)) _dwDropEffect = DROPEFFECT_LINK | DROPEFFECT_COPY; _dwDropEffect |= GetPreferedDropEffect(pdtobj); } } } *pdwEffect &= _dwDropEffect; } return S_OK; } HRESULT CBandSite::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { TraceMsg(TF_BANDDD, "CBandSite::DragOver %d %d", ptl.x, ptl.y); if (_fDragSource) { RECT rc; POINT pt; pt.x = ptl.x; pt.y = ptl.y; GetWindowRect(_hwnd, &rc); if (PtInRect(&rc, pt)) SendMessage(_hwnd, RB_DRAGMOVE, 0, (LPARAM)-1); } else { *pdwEffect &= _dwDropEffect; } return S_OK; } HRESULT CBandSite::DragLeave(void) { return S_OK; } HRESULT CBandSite::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { HRESULT hres = E_FAIL; TraceMsg(TF_BANDDD, "CBandSite::Drop"); if (_fDragSource) { SendMessage(_hwnd, RB_ENDDRAG, 0, 0); *pdwEffect = DROPEFFECT_NONE; hres = S_OK; } else { FORMATETC fmte = {g_cfDeskBand, NULL, 0, -1, TYMED_ISTREAM}; STGMEDIUM stg; IUnknown *punk = NULL; LPITEMIDLIST pidl; // if it was an object of our type, create it! if ((*pdwEffect & DROPEFFECT_MOVE) && SUCCEEDED(pdtobj->GetData(&fmte, &stg))) { hres = OleLoadFromStream(stg.pstm, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hres)) { *pdwEffect = DROPEFFECT_MOVE; } ReleaseStgMedium(&stg); } else if ((*pdwEffect & (DROPEFFECT_COPY | DROPEFFECT_LINK)) && SUCCEEDED(SHPidlFromDataObject(pdtobj, &pidl, NULL, 0))) { hres = SHCreateBandForPidl(pidl, &punk, (grfKeyState & (MK_CONTROL | MK_SHIFT)) == (MK_CONTROL | MK_SHIFT)); ILFree(pidl); if (SUCCEEDED(hres)) { if (*pdwEffect & DROPEFFECT_LINK) *pdwEffect = DROPEFFECT_LINK; else *pdwEffect = DROPEFFECT_COPY; } } if (punk) { hres = _AddBand(punk); if (SUCCEEDED(hres)) { DWORD dwState; dwState = IDataObject_GetDeskBandState(pdtobj); SetBandState(ShortFromResult(hres), BSSF_NOTITLE, dwState & BSSF_NOTITLE); } punk->Release(); } } if (FAILED(hres)) *pdwEffect = DROPEFFECT_NONE; return hres; } // } //*** ::_MergeBS -- merge two bandsites into one // ENTRY/EXIT // pdtDst [INOUT] destination DropTarget (always from bandsite) // pbsSrc [INOUT] source bandsite; deleted if all bands moved successfully // ret S_OK if all bands moved; S_FALSE if some moved; E_* o.w. // NOTES // note that if all the bands are moved successfully, pbsSrc will be deleted // as a side-effect. // pdtDst is assumed to accept multiple drops (bandsite does). // pdtDst may be from marshal/unmarshal (tray bandsite). HRESULT _MergeBS(IDropTarget *pdtDst, IBandSite *pbsSrc) { HRESULT hres = E_FAIL; DWORD idBand; pbsSrc->AddRef(); // don't go away until we're all done! // drag each band in turn while (SUCCEEDED(pbsSrc->EnumBands(0, &idBand))) { // note our (bogus?) assumption that bands which can't be // dragged will percolate down to a contiguous range of // iBands 0..n. if that's bogus i'm not sure how we can // keep track of where we are. IDataObject *pdoSrc; hres = pbsSrc->GetBandObject(idBand, IID_PPV_ARG(IDataObject, &pdoSrc)); if (SUCCEEDED(hres)) { DWORD dwEffect = DROPEFFECT_MOVE | DROPEFFECT_COPY; hres = SHSimulateDrop(pdtDst, pdoSrc, 0, NULL, &dwEffect); pdoSrc->Release(); if (SUCCEEDED(hres) && (dwEffect & DROPEFFECT_MOVE)) { hres = pbsSrc->RemoveBand(idBand); ASSERT(SUCCEEDED(hres)); } } // we failed to move the band, bail if (FAILED(hres)) { ASSERT(0); break; } } pbsSrc->Release(); TraceMsg(DM_DRAG, "dba.ms: ret hres=%x", hres); return hres; } void CBandSite::_BandItemEnumCallback(int dincr, PFNBANDITEMENUMCALLBACK pfnCB, void *pv) { UINT iFirst = 0; ASSERT(dincr == 1 || dincr == -1); if (dincr < 0) { iFirst = _GetBandItemCount() - 1; // start from last } for (UINT i = iFirst; i < (UINT) _GetBandItemCount(); i += dincr) { CBandItemData *pbid = _GetBandItem(i); if (pbid) { BOOL fStop = !pfnCB(pbid, pv); pbid->Release(); if (fStop) break; } } } void CBandSite::_DeleteAllBandItems() { for (int i = _GetBandItemCount() - 1; i >= 0; i--) { CBandItemData *pbid = _GetBandItem(i); // Release the banditem data first, while it can still // receive cleanup notifications from its control. *Then* // delete the band item. if (pbid) { _ReleaseBandItemData(pbid, i); pbid->Release(); } // REARCHITECT: chrisfra 5/13/97 if you skip deleting, rebar can // rearrange on delete, moving a band so that it is never seen // and consequently we leak BrandBand and much else _DeleteBandItem(i); // unhook from host (rebar) } } CBandItemData *CBandSite::_GetBandItem(int i) { REBARBANDINFO rbbi = { 0 }; rbbi.cbSize = sizeof(rbbi); rbbi.fMask = RBBIM_LPARAM; rbbi.lParam = NULL; // in case of failure below if (_hwnd) SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi); CBandItemData *pbid = (CBandItemData *)rbbi.lParam; if (pbid) { pbid->AddRef(); } return pbid; } int CBandSite::_GetBandItemCount() { int cel = 0; if (_hwnd) { ASSERT(IS_VALID_HANDLE(_hwnd, WND)); cel = (int)SendMessage(_hwnd, RB_GETBANDCOUNT, 0, 0); } return cel; } void CBandSite::_GetBandInfo(CBandItemData *pbid, DESKBANDINFO *pdbi) { pdbi->dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR; pdbi->ptMinSize = pbid->ptMinSize; pdbi->ptMaxSize = pbid->ptMaxSize; pdbi->ptIntegral = pbid->ptIntegral; pdbi->ptActual = pbid->ptActual; StrCpyNW(pdbi->wszTitle, pbid->szTitle, ARRAYSIZE(pdbi->wszTitle)); pdbi->dwModeFlags = pbid->dwModeFlags; pdbi->crBkgnd = pbid->crBkgnd; if (pbid->pdb) { pbid->pdb->GetBandInfo(pbid->dwBandID, _dwMode, pdbi); } if (pdbi->wszTitle[0] == 0) { pdbi->dwMask &= ~DBIM_TITLE; } pbid->ptMinSize = pdbi->ptMinSize; pbid->ptMaxSize = pdbi->ptMaxSize; pbid->ptIntegral = pdbi->ptIntegral; pbid->ptActual = pdbi->ptActual; StrCpyNW(pbid->szTitle, pdbi->wszTitle, ARRAYSIZE(pbid->szTitle)); pbid->dwModeFlags = pdbi->dwModeFlags; pbid->crBkgnd = pdbi->crBkgnd; if (!(pdbi->dwMask & DBIM_TITLE)) // title not supported pbid->fNoTitle = TRUE; ASSERT(pdbi->dwModeFlags & DBIMF_VARIABLEHEIGHT ? pbid->ptIntegral.y : TRUE); } void CBandSite::_BandInfoFromBandItem(REBARBANDINFO *prbbi, CBandItemData *pbid, BOOL fBSOnly) { // REVIEW: could be optimized more DESKBANDINFO dbi; if (!fBSOnly) _GetBandInfo(/*INOUT*/ pbid, &dbi); // now add the view as a band in the rebar // add links band prbbi->fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_IDEALSIZE | RBBIM_TEXT; if (fBSOnly) prbbi->fMask = RBBIM_STYLE|RBBIM_TEXT; // clear the bits the are band settable prbbi->fStyle |= RBBS_FIXEDBMP; prbbi->fStyle &= ~(RBBS_NOGRIPPER | RBBS_GRIPPERALWAYS | RBBS_VARIABLEHEIGHT | RBBS_USECHEVRON); if (_dwStyle & BSIS_NOGRIPPER) prbbi->fStyle |= RBBS_NOGRIPPER; else if (_dwStyle & BSIS_ALWAYSGRIPPER) prbbi->fStyle |= RBBS_GRIPPERALWAYS; else { // BSIS_AUTOGRIPPER... if (!(prbbi->fStyle & RBBS_FIXEDSIZE) && !(_dwMode & DBIF_VIEWMODE_FLOATING)) prbbi->fStyle |= RBBS_GRIPPERALWAYS; } if (pbid->dwModeFlags & DBIMF_VARIABLEHEIGHT) prbbi->fStyle |= RBBS_VARIABLEHEIGHT; if (pbid->dwModeFlags & DBIMF_USECHEVRON) prbbi->fStyle |= RBBS_USECHEVRON; if (pbid->dwModeFlags & DBIMF_BREAK) prbbi->fStyle |= RBBS_BREAK; if (pbid->dwModeFlags & DBIMF_TOPALIGN) prbbi->fStyle |= RBBS_TOPALIGN; if (!fBSOnly) { prbbi->hwndChild = pbid->hwnd; prbbi->wID = pbid->dwBandID; // set up the geometries prbbi->cxMinChild = pbid->ptMinSize.x; prbbi->cyMinChild = pbid->ptMinSize.y; prbbi->cyMaxChild = pbid->ptMaxSize.y; prbbi->cyIntegral = pbid->ptIntegral.y; if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) { // after we're up, it's the "ideal" point prbbi->cxIdeal = pbid->ptActual.y; } else { // after we're up, it's the "ideal" point prbbi->cxIdeal = pbid->ptActual.x; } if (prbbi->cxIdeal == (UINT)-1) prbbi->cxIdeal = 0; if (pbid->dwModeFlags & DBIMF_BKCOLOR) { if (dbi.dwMask & DBIM_BKCOLOR) { prbbi->fMask |= RBBIM_COLORS; prbbi->clrFore = CLR_DEFAULT; prbbi->clrBack = dbi.crBkgnd; } } ASSERT(pbid->fNoTitle || (dbi.dwMask & DBIM_TITLE)); // pbid in sync? } SHUnicodeToTChar(pbid->szTitle, prbbi->lpText, prbbi->cch); if (!pbid->fNoTitle && _IsEnableTitle(pbid) && !(_dwStyle & BSIS_NOCAPTION)) { prbbi->fStyle &= ~RBBS_HIDETITLE; } else { // No text please prbbi->fStyle |= RBBS_HIDETITLE; } // Make this band a tabstop. Itbar will override v_SetTabstop // since for the browser we don't want every band to be a tabstop. v_SetTabstop(prbbi); } void CBandSite::v_SetTabstop(LPREBARBANDINFO prbbi) { // We specify that a band should be a tabstop by setting the WS_TABSTOP // bit. Never make RBBS_FIXEDSIZE bands (i.e., the brand) tabstops. if (prbbi && prbbi->hwndChild && !(prbbi->fStyle & RBBS_FIXEDSIZE)) SHSetWindowBits(prbbi->hwndChild, GWL_STYLE, WS_TABSTOP, WS_TABSTOP); } //*** cbs::_IsEnableTitle -- should we enable (ungray) title // DESCRIPTION // used for handing back title and for enabling menu // NOTES // pbid unused... // #ifndef UNIX _inline #endif BOOL CBandSite::_IsEnableTitle(CBandItemData *pbid) { ASSERT(pbid); return (/*pbid && !pbid->fNoTitle &&*/ !((_dwMode & DBIF_VIEWMODE_FLOATING) && _GetBandItemCount() <= 1)); } BOOL CBandSite::_UpdateBandInfo(CBandItemData *pbid, BOOL fBSOnly) { REBARBANDINFO rbbi = {sizeof(rbbi)}; int iRB = _BandIDToIndex(pbid->dwBandID); // now update the info rbbi.fMask = RBBIM_ID | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_STYLE; if (fBSOnly) rbbi.fMask = RBBIM_STYLE; SendMessage(_hwnd, RB_GETBANDINFO, iRB, (LPARAM)&rbbi); if (!fBSOnly) { if (_dwMode & (DBIF_VIEWMODE_FLOATING | DBIF_VIEWMODE_VERTICAL)) { pbid->ptActual.x = rbbi.cyChild; pbid->ptActual.y = rbbi.cxIdeal; } else { pbid->ptActual.x = rbbi.cxIdeal; pbid->ptActual.y = rbbi.cyChild; } pbid->ptMinSize.x = rbbi.cxMinChild; pbid->ptMinSize.y = rbbi.cyMinChild; pbid->ptMaxSize.y = rbbi.cyMaxChild; } TCHAR szBand[40]; rbbi.lpText = szBand; rbbi.cch = ARRAYSIZE(szBand); _BandInfoFromBandItem(&rbbi, pbid, fBSOnly); return BOOLFROMPTR(SendMessage(_hwnd, RB_SETBANDINFO, (UINT)iRB, (LPARAM)&rbbi)); } BOOL CBandSite::_AddBandItem(CBandItemData *pbid) { REBARBANDINFO rbbi = {sizeof(rbbi)}; pbid->ptActual.x = -1; pbid->ptActual.y = -1; TCHAR szBand[40]; rbbi.lpText = szBand; rbbi.cch = ARRAYSIZE(szBand); _BandInfoFromBandItem(&rbbi, pbid, FALSE); rbbi.cyChild = pbid->ptActual.y; rbbi.fMask |= RBBIM_LPARAM; rbbi.lParam = (LPARAM)pbid; ASSERT(rbbi.fMask & RBBIM_ID); return BOOLFROMPTR(SendMessage(_hwnd, RB_INSERTBAND, (UINT) (pbid->dwModeFlags & DBIMF_ADDTOFRONT) ? 0 : -1, (LPARAM)&rbbi)); } void CBandSite::_DeleteBandItem(int i) { SendMessage(_hwnd, RB_DELETEBAND, i, 0); } DWORD CBandSite::_IndexToBandID(int i) { REBARBANDINFO rbbi = {sizeof(rbbi)}; rbbi.fMask = RBBIM_ID; if (SendMessage(_hwnd, RB_GETBANDINFO, i, (LPARAM)&rbbi)) return rbbi.wID; else return -1; } /*---------------------------------------------------------- Purpose: Given the band ID, returns the internal band index. */ int CBandSite::_BandIDToIndex(DWORD dwBandID) { int nRet = -1; if (_hwnd) nRet = (int)SendMessage(_hwnd, RB_IDTOINDEX, (WPARAM) dwBandID, (LPARAM) 0); return nRet; } /*---------------------------------------------------------- Purpose: The Parent Site may want to override what the admin specified. Return Values: S_OK: Do lock band. S_FALSE: Do NOT Lock band. */ HRESULT CBandSite::_IsRestricted(DWORD dwBandID, DWORD dwRestrictAction, DWORD dwBandFlags) { HRESULT hr; DWORD dwRestrictionAction; hr = IUnknown_HandleIRestrict(_punkSite, &RID_RDeskBars, dwRestrictAction, NULL, &dwRestrictionAction); if (RR_NOCHANGE == dwRestrictionAction) // If our parent didn't handle it, we will. dwRestrictionAction = IsFlagSet(_GetAdminSettings(dwBandID), dwBandFlags) ? RR_DISALLOW : RR_ALLOW; if (RR_DISALLOW == dwRestrictionAction) hr = S_OK; else hr = S_FALSE; ASSERT(SUCCEEDED(hr)); // FAIL(hr) other than hr == E_NOTIMPLE; is not good. return hr; } BOOL ConfirmRemoveBand(HWND hwnd, UINT uID, LPCTSTR pszName) { TCHAR szTemp[1024], szTitle[80]; BOOL bRet = TRUE; MLLoadString(IDS_CONFIRMCLOSETITLE, szTitle, ARRAYSIZE(szTitle)); // Calling FormatMessage with FORMAT_MESSAGE_FROM_HMODULE fails MLLoadString(uID, szTemp, ARRAYSIZE(szTemp)); DWORD cchLen = lstrlen(szTemp) + lstrlen(pszName) + 1; LPTSTR pszTemp2 = (TCHAR *)LocalAlloc(LPTR, cchLen * sizeof(TCHAR)); if (pszTemp2 != NULL) { _FormatMessage(szTemp, pszTemp2, cchLen, pszName); MLLoadString(IDS_CONFIRMCLOSETEXT, szTemp, ARRAYSIZE(szTemp)); cchLen = lstrlen(szTemp) + lstrlen(pszTemp2) + 1; LPTSTR pszStr = (TCHAR *)LocalAlloc(LPTR, cchLen * sizeof(TCHAR)); if (pszStr != NULL) { _FormatMessage(szTemp, pszStr, cchLen, pszTemp2); bRet = (IDOK == SHMessageBoxCheck(hwnd, pszStr, szTitle, MB_OKCANCEL, IDOK, TEXT("WarnBeforeCloseBand"))); LocalFree(pszStr); } LocalFree(pszTemp2); } return bRet; }