|
|
#include "priv.h"
#include "sccls.h"
#include "resource.h"
#include "mshtmhst.h"
#include "deskbar.h"
#include "bands.h"
#define WANT_CBANDSITE_CLASS
#include "bandsite.h"
#include <trayp.h> // TM_*
#include <desktray.h> // IDeskTray
#include "dbapp.h"
#include "mluisupp.h"
/*
this virtual app implments DeskBars that you have on the desktop. it has the glue that combines CDeskBar with CBandSite and populates the bands (as well as persistance and such) -Chee */
#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_TRAY 0 // tray: marshal, side, etc.
#ifdef DEBUG
extern unsigned long DbStreamTell(IStream *pstm); #else
#define DbStreamTell(pstm) ((ULONG) 0)
#endif
#define SUPERCLASS CDeskBar
/*
Instead of just 4 Deskbars on the whole desktop, we now have 4 deskbars for each monitor, however, this brings problem whenever a monitor goes away, we need to clean up the following datastructure. - dli */
// FEATURE: (dli) maybe this should be moved into multimon.h
// however, people should not get into the habbit of depending on this.
// and it's really not used anywhere else, so, keep it here for now.
#define DSA_MONITORSGROW 1
typedef struct DeskBarsPerMonitor { HMONITOR hMon; IDeskBar* Deskbars[4]; } DESKBARSPERMONITOR, *LPDESKBARSPERMONITOR;
HDSA g_hdsaDeskBars = NULL;
enum ips_e { IPS_FALSE, // reserved, must be 0 (FALSE)
IPS_LOAD, IPS_INITNEW };
CASSERT(IPS_FALSE == 0);
CDeskBarApp::~CDeskBarApp() { _LeaveSide(); if (_pbs) _pbs->Release(); if (_pcm) _pcm->Release(); }
LRESULT CDeskBarApp::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lres = SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
if (!_hwnd) { return lres; // destroyed by superclass
}
if (_eMode == WBM_BFLOATING) { switch (uMsg) { case WM_NOTIFY: { //
// override the hittest value to be HTCAPTION if we're docked browser based
//
NMHDR* pnm = (NMHDR*)lParam; if (pnm->code == NM_NCHITTEST && pnm->hwndFrom == _hwndChild) { //
// in the floating bug docked int he browser, we don't do
// mdi child stuff, so we make the gripper work as the caption
//
NMMOUSE* pnmm = (NMMOUSE*)pnm; if (pnmm->dwHitInfo == RBHT_CAPTION || pnmm->dwHitInfo == RBHT_GRABBER) lres = HTTRANSPARENT; } } break; case WM_NCHITTEST: // all "client" areas are captions in this mode
if (lres == HTCLIENT) lres = HTCAPTION; break; case WM_SETCURSOR: DefWindowProcWrap(hwnd, uMsg, wParam, lParam); return TRUE; } } return lres; }
BOOL CDeskBarApp::_OnCloseBar(BOOL fConfirm) { // if we are closing a bar with no bands in it, don't pop up the dialog
if ((_pbs && (_pbs->EnumBands(-1,NULL)==0)) || (!fConfirm || ConfirmRemoveBand(_hwnd, IDS_CONFIRMCLOSEBAR, TEXT(""))) ) return SUPERCLASS::_OnCloseBar(FALSE); return FALSE; }
// Gets the Deskbars on a specific monitor
// DBPM -- DeskBars Per Monitor
LPDESKBARSPERMONITOR GetDBPMWithMonitor(HMONITOR hMon, BOOL fCreate) { int ihdsa; LPDESKBARSPERMONITOR pdbpm;
if (!g_hdsaDeskBars) { if (fCreate) g_hdsaDeskBars = DSA_Create(SIZEOF(DESKBARSPERMONITOR), DSA_MONITORSGROW); }
if (!g_hdsaDeskBars) return NULL; // If we find the DBPM with this HMONITOR, return it.
for (ihdsa = 0; ihdsa < DSA_GetItemCount(g_hdsaDeskBars); ihdsa++) { pdbpm = (LPDESKBARSPERMONITOR)DSA_GetItemPtr(g_hdsaDeskBars, ihdsa); if (pdbpm->hMon == hMon) return pdbpm; }
if (fCreate) { DESKBARSPERMONITOR dbpm = {0}; // This monitor is not setup, so set it, and set us the
// the ownder of _uSide
dbpm.hMon = hMon; ihdsa = DSA_AppendItem(g_hdsaDeskBars, &dbpm); pdbpm = (LPDESKBARSPERMONITOR)DSA_GetItemPtr(g_hdsaDeskBars, ihdsa); return pdbpm; } // When all else fails, return NULL
return NULL; } void CDeskBarApp::_LeaveSide() { if (ISABE_DOCK(_uSide) && !ISWBM_FLOAT(_eMode)) { // remove ourselves from the array list of where we were
LPDESKBARSPERMONITOR pdbpm = GetDBPMWithMonitor(_hMon, FALSE); if (pdbpm && (pdbpm->Deskbars[_uSide] == this)) { ASSERT(pdbpm->hMon); ASSERT(pdbpm->hMon == _hMon); pdbpm->Deskbars[_uSide] = NULL; } } }
//***
// NOTES
// FEATURE: should we create/use IDeskTray::AppBarGetState?
UINT GetTraySide(HMONITOR * phMon) { LRESULT lTmp; APPBARDATA abd; abd.cbSize = sizeof(APPBARDATA); abd.hWnd = GetTrayWindow(); if (phMon) Tray_GetHMonitor(abd.hWnd, phMon);
abd.uEdge = (UINT)-1; //lTmp = g_pdtray->AppBarGetTaskBarPos(&abd);
lTmp = SHAppBarMessage(ABM_GETTASKBARPOS, &abd); ASSERT(lTmp); TraceMsg(DM_TRAY, "gts: ret=ABE_%d", abd.uEdge); return abd.uEdge; }
//***
// ENTRY/EXIT
// fNoMerge is for the IPS::Load case
// NOTES
// warning: be careful of reentrancy! fNoMove is how we guard against it.
void CDeskBarApp::_SetModeSide(UINT eMode, UINT uSide, HMONITOR hMonNew, BOOL fNoMerge) { BOOL fNoMove;
// make sure we don't merge etc. on NOOP moves.
// we do such moves to force refresh (e.g. for autohide and IPS::Load);
// also happens w/ drags which end up back where they started
fNoMove = (eMode == _eMode && uSide == _uSide && hMonNew == _hMon);
if (!fNoMove) _LeaveSide(); // warning: this may call (e.g.) AppBarRegister, which causes a
// resize, which calls back to us. careful of reentrancy!!!
// if we do reenter we end up w/ nt5:155043, where entry #1 has
// fNoMove==0, then we get a recalc, entry #2 has fNoMove==1,
// and we set our side array to us, then return back to entry
// #1 which merges into itself!
SUPERCLASS::_SetModeSide(eMode, uSide, hMonNew, fNoMerge);
if (!fNoMove) { if (ISABE_DOCK(_uSide) && !ISWBM_FLOAT(_eMode)) { LPDESKBARSPERMONITOR pdbpm = GetDBPMWithMonitor(hMonNew, TRUE); HMONITOR hMonTray = NULL; if (pdbpm) { if (fNoMerge) { if (!pdbpm->Deskbars[_uSide]) { // 1st guy on an edge owns it
// if we don't do this, when we load persisted state on logon
// we end up w/ *no* edge owner (since fNoMerge), so we don't
// merge on subsequent moves.
goto Lsetowner; } } else if (pdbpm->Deskbars[_uSide]) { // if someone already there, try merging into them
#ifdef DEBUG
// alt+drag suppresses merge
// DEBUG only since don't track >1 per side, but useful
// for testing appbars and toolbars anyway
if (!(GetKeyState(VK_MENU) < 0)) #endif
{ extern IBandSite* _GetBandSite(IDeskBar * pdb); IBandSite *pbs; pbs = _GetBandSite(pdbpm->Deskbars[_uSide]); // nt5:215952: should 'never' have pbs==0 but somehow
// it does happen (during deskbar automation tests).
// call andyp or tjgreen if you hit this assert so
// we can figure out why.
if (TPTR(pbs)) { _MergeSide(pbs); // dst=pbs, src=this
pbs->Release(); } } } else if ((GetTraySide(&hMonTray) == _uSide) && (hMonTray == _hMon) && !(GetKeyState(VK_SHIFT) < 0)) { // ditto for tray (but need to marshal/unmarshal)
#ifdef DEBUG
// alt+drag suppresses merge
// DEBUG only since don't track >1 per side, but useful
// for testing appbars and toolbars anyway
if (!(GetKeyState(VK_MENU) < 0)) #endif
{ _MergeSide((IBandSite *)1); // dst=pbs, src=this
} } else { // o.w. nobody there yet, set ourselves as owner
ASSERT(pdbpm->hMon); ASSERT(pdbpm->hMon == hMonNew); Lsetowner: TraceMsg(DM_TRAY, "cdba._sms: 1st side owner this=0x%x", this); pdbpm->Deskbars[_uSide] = this; } } } } }
void CDeskBarApp::_UpdateCaptionTitle() { if (ISWBM_FLOAT(_eMode)) { int iCount = (int)_pbs->EnumBands((UINT)-1, NULL); if (iCount == 1) { DWORD dwBandID; if (SUCCEEDED(_pbs->EnumBands(0, &dwBandID))) { WCHAR wszTitle[80]; if (SUCCEEDED(_pbs->QueryBand(dwBandID, NULL, NULL, wszTitle, ARRAYSIZE(wszTitle)))) { SetWindowText(_hwnd, wszTitle); } } } else { TCHAR szTitle[80]; szTitle[0] = 0; MLLoadString(IDS_WEBBARSTITLE,szTitle,ARRAYSIZE(szTitle)); SetWindowText(_hwnd, szTitle); } } }
void CDeskBarApp::_NotifyModeChange(DWORD dwMode) { SUPERCLASS::_NotifyModeChange(dwMode); _UpdateCaptionTitle(); }
//*** GetTrayIface -- get iface from tray (w/ marshal/unmarshal)
//
HRESULT GetTrayIface(REFIID riid, void **ppvObj) { HRESULT hr = E_FAIL; HWND hwndTray; IStream *pstm;
TraceMsg(DM_TRAY, "gtif: marshal!");
*ppvObj = NULL;
hwndTray = GetTrayWindow(); if (hwndTray) { pstm = (IStream *) SendMessage(hwndTray, TM_MARSHALBS, (WPARAM)(GUID *)&riid, 0);
if (EVAL(pstm)) { // paired w/ matching Marshal in explorer (TM_MARSHALBS)
hr = CoGetInterfaceAndReleaseStream(pstm, riid, ppvObj); ASSERT(SUCCEEDED(hr)); } }
return hr; }
//*** _MergeSide -- merge two deskbars into one
// ENTRY/EXIT
// this [INOUT] destination deskbar (ptr:1 if tray)
// pdbSrc [INOUT] source deskbar; deleted if all bands moved successfully
// ret S_OK if all bands moved; S_FALSE if some moved; E_* o.w.
HRESULT CDeskBarApp::_MergeSide(IBandSite *pbsDst) { extern HRESULT _MergeBS(IDropTarget *pdtDst, IBandSite *pbsSrc); HRESULT hr; IDropTarget *pdtDst;
AddRef(); // make sure we don't disappear partway thru operation
if (pbsDst == (IBandSite *)1) { // get (marshal'ed) iface from tray
hr = GetTrayIface(IID_IDropTarget, (void **)&pdtDst); ASSERT(SUCCEEDED(hr)); } else { // don't merge into ourself!
ASSERT(pbsDst != _pbs); ASSERT(!SHIsSameObject(pbsDst, SAFECAST(_pbs, IBandSite*)));
hr = pbsDst->QueryInterface(IID_IDropTarget, (void **)&pdtDst); ASSERT(SUCCEEDED(hr)); } ASSERT(SUCCEEDED(hr) || pdtDst == NULL);
if (pdtDst) { hr = _MergeBS(pdtDst, _pbs); pdtDst->Release(); }
Release();
return hr; }
void CDeskBarApp::_CreateBandSiteMenu() { CoCreateInstance(CLSID_BandSiteMenu, NULL,CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu3, &_pcm)); if (_pcm) { IShellService* pss; _pcm->QueryInterface(IID_IShellService, (LPVOID*)&pss); if (pss) { pss->SetOwner((IBandSite*)_pbs); pss->Release(); } } }
HRESULT CDeskBarApp::QueryInterface(REFIID riid, LPVOID * ppvObj) { if (IsEqualIID(riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2) || IsEqualIID(riid, IID_IContextMenu3)) { if (!_pcm) { _CreateBandSiteMenu(); } // only return out our pointer if we got the one we're going
// to delegate to
if (_pcm) { *ppvObj = SAFECAST(this, IContextMenu3*); AddRef(); return S_OK; } } return SUPERCLASS::QueryInterface(riid, ppvObj); }
HRESULT CDeskBarApp::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { if (IsEqualGUID(guidService,SID_SBandSite)) { return QueryInterface(riid, ppvObj); } return SUPERCLASS::QueryService(guidService, riid, ppvObj); }
HRESULT CDeskBarApp::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { int idCmd = -1;
if (!HIWORD(pici->lpVerb)) idCmd = LOWORD(pici->lpVerb);
if (idCmd >= _idCmdDeskBarFirst) { _AppBarOnCommand(idCmd - _idCmdDeskBarFirst); return S_OK; } return _pcm->InvokeCommand(pici); }
HRESULT CDeskBarApp::GetCommandString( UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) { return _pcm->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax); }
HRESULT CDeskBarApp::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) { return _pcm->HandleMenuMsg(uMsg, wParam, lParam); }
HRESULT CDeskBarApp::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plres) { return _pcm->HandleMenuMsg2(uMsg, wParam, lParam, plres); }
HRESULT CDeskBarApp::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hr = _pcm->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); if (SUCCEEDED(hr)) { int i = hr; HMENU hmenuSrc;
_idCmdDeskBarFirst = i; hmenuSrc = _GetContextMenu();
// off-by-1 and by idCmdFirst+i, i think...
i += Shell_MergeMenus(hmenu, hmenuSrc, (UINT)-1, idCmdFirst + i, idCmdLast, MM_ADDSEPARATOR) - (idCmdFirst + i); DestroyMenu(hmenuSrc);
return ResultFromShort(i); // potentially off-by-1, but who cares...
} return hr; }
//***
// NOTES
// FEATURE: nuke this, fold it into CDeskBarApp_CreateInstance
HRESULT DeskBarApp_Create(IUnknown** ppunk) { HRESULT hres;
*ppunk = NULL; CDeskBarApp *pdb = new CDeskBarApp(); if (!pdb) return E_OUTOFMEMORY; CBandSite *pcbs = new CBandSite(NULL); if (pcbs) { IDeskBarClient *pdbc = SAFECAST(pcbs, IDeskBarClient*); hres = pdb->SetClient(pdbc); if (SUCCEEDED(hres)) { pdb->_pbs = pcbs; pcbs->AddRef(); *ppunk = SAFECAST(pdb, IDeskBar*); } pdbc->Release(); } else { hres = E_OUTOFMEMORY; }
if (FAILED(hres)) pdb->Release(); return hres; }
STDAPI CDeskBarApp_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { HRESULT hres; IUnknown *punk;
// aggregation checking is handled in class factory
hres = DeskBarApp_Create(&punk); if (SUCCEEDED(hres)) { *ppunk = SAFECAST(punk, IDockingWindow*); return S_OK; }
return E_OUTOFMEMORY; }
//*** CDeskBarApp::IInputObject*::* {
//
HRESULT CDeskBarApp::TranslateAcceleratorIO(LPMSG lpMsg) { if (lpMsg->message == WM_SYSKEYDOWN) { if (lpMsg->wParam == VK_F4) { // ie4:28819: need to trap VK_F4 here, o.w. CBaseBrowser::TA
// does a last-chance (winsdk)::TA (to IDM_CLOSE) and doing a
// shutdown!
PostMessage(_hwnd, WM_CLOSE, 0, 0); return S_OK; } }
return SUPERCLASS::TranslateAcceleratorIO(lpMsg); }
// }
//*** CDeskBarApp::IPersistStream*::* {
//
HRESULT CDeskBarApp::GetClassID(CLSID *pClassID) { *pClassID = CLSID_DeskBarApp; return S_OK; }
HRESULT CDeskBarApp::IsDirty(void) { return S_FALSE; // Never be dirty
}
//
// Persisted CDeskBarApp
//
#define STC_VERSION 1
struct SThisClass { DWORD cbSize; DWORD cbVersion; };
HRESULT CDeskBarApp::Load(IStream *pstm) { SThisClass stc; ULONG cbRead; HRESULT hres;
TraceMsg(DM_PERSIST, "cdba.l enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm));
ASSERT(!_eInitLoaded); _eInitLoaded = IPS_LOAD;
hres = pstm->Read(&stc, SIZEOF(stc), &cbRead); #ifdef DEBUG
// just in case we toast ourselves (offscreen or something)...
static BOOL fNoPersist = FALSE; if (fNoPersist) hres = E_FAIL; #endif
if (hres==S_OK && cbRead==SIZEOF(stc)) { if (stc.cbSize==SIZEOF(SThisClass) && stc.cbVersion==STC_VERSION) { _eInitLoaded = IPS_LOAD; // FEATURE: what if OLFS of bands fails?
hres = SUPERCLASS::Load(pstm);
TraceMsg(DM_INIT, "cdba::Load succeeded"); } else { TraceMsg(DM_ERROR, "cdba::Load failed stc.cbSize==SIZEOF(SThisClass) && stc.cbVersion==SWB_VERSION"); hres = E_FAIL; } } else { TraceMsg(DM_ERROR, "cdba::Load failed (hres==S_OK && cbRead==SIZEOF(_adEdge)"); hres = E_FAIL; } TraceMsg(DM_PERSIST, "cdba.l leave tell()=%x", DbStreamTell(pstm)); // after loading this, if we find that we're supposed to be browser docked,
// make our bandsite always have a gripper
if (_eMode == WBM_BFLOATING) { BANDSITEINFO bsinfo;
bsinfo.dwMask = BSIM_STYLE; bsinfo.dwStyle = BSIS_ALWAYSGRIPPER;
_pbs->SetBandSiteInfo(&bsinfo); } return hres; }
HRESULT CDeskBarApp::Save(IStream *pstm, BOOL fClearDirty) { HRESULT hres; SThisClass stc;
TraceMsg(DM_PERSIST, "cdba.s enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm)); stc.cbSize = SIZEOF(SThisClass); stc.cbVersion = STC_VERSION;
hres = pstm->Write(&stc, SIZEOF(stc), NULL); if (SUCCEEDED(hres)) { SUPERCLASS::Save(pstm, fClearDirty); } TraceMsg(DM_PERSIST, "cdba.s leave tell()=%x", DbStreamTell(pstm)); return hres; }
HRESULT CDeskBarApp::GetSizeMax(ULARGE_INTEGER *pcbSize) { ULARGE_INTEGER cbMax = { SIZEOF(SThisClass), 0 }; *pcbSize = cbMax; return S_OK; }
HRESULT CDeskBarApp::InitNew(void) { HRESULT hres;
ASSERT(!_eInitLoaded); _eInitLoaded = IPS_INITNEW; TraceMsg(DM_INIT, "CDeskBarApp::InitNew called");
hres = SUPERCLASS::InitNew(); if (FAILED(hres)) return hres;
// can't call _InitPos4 until set site in SetSite
return hres; }
HRESULT CDeskBarApp::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { if (pguidCmdGroup == NULL) { /*NOTHING*/ } else if (IsEqualGUID(CGID_DeskBarClient, *pguidCmdGroup)) { switch (nCmdID) { case DBCID_EMPTY: if (_pbs) { // if we have no bands left, close
PostMessage(_hwnd, WM_CLOSE, 0, 0); } return S_OK; } } else if (IsEqualIID(*pguidCmdGroup, CGID_DeskBand)) { switch (nCmdID) { case DBID_BANDINFOCHANGED: _UpdateCaptionTitle(); return S_OK; } } else if (IsEqualIID(*pguidCmdGroup, CGID_BandSite)) { switch (nCmdID) { case BSID_BANDADDED: case BSID_BANDREMOVED: _UpdateCaptionTitle(); return S_OK; } }
return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); }
HRESULT CDeskBarApp::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog) { HRESULT hres;
ASSERT(!_eInitLoaded); _eInitLoaded = IPS_LOAD; TraceMsg(DM_INIT, "CDeskBarApp::Load(bag) called");
hres = SUPERCLASS::Load(pPropBag, pErrorLog); // after loading this, if we find that we're supposed to be browser docked,
// make our bandsite always have a gripper
if (_eMode == WBM_BFLOATING) { BANDSITEINFO bsinfo;
bsinfo.dwMask = BSIM_STYLE; bsinfo.dwStyle = BSIS_ALWAYSGRIPPER;
_pbs->SetBandSiteInfo(&bsinfo); } return hres; }
IBandSite * _GetBandSite(IDeskBar * pdb) { IBandSite* pbs = NULL; if (pdb) { IUnknown* punkClient; pdb->GetClient(&punkClient); if (punkClient) { punkClient->QueryInterface(IID_IBandSite, (LPVOID*)&pbs); punkClient->Release(); } } return pbs; }
IBandSite* DeskBarApp_GetBandSiteOnEdge(UINT uEdge) { // APPCOMPAT: (dli) if no HMONITOR is passed in, use the primary monitor
// should make sure that there is always a valid HMONITOR passed in
HMONITOR hMon = GetPrimaryMonitor(); // --------------------------------------------------------------
LPDESKBARSPERMONITOR pdbpm = GetDBPMWithMonitor(hMon, FALSE); if (pdbpm) { ASSERT(pdbpm->hMon); ASSERT(pdbpm->hMon == hMon); return _GetBandSite(pdbpm->Deskbars[uEdge]); } return NULL; }
IBandSite* DeskBarApp_GetBandSiteAtPoint(LPPOINT ppt) { HWND hwnd = WindowFromPoint(*ppt); HMONITOR hMon = MonitorFromPoint(*ppt, MONITOR_DEFAULTTONULL); if (hwnd && hMon) { LPDESKBARSPERMONITOR pdbpm = GetDBPMWithMonitor(hMon, FALSE); if (pdbpm) { ASSERT(pdbpm->hMon); ASSERT(pdbpm->hMon == hMon); int i; for (i = 0; i < 4; i++) { if (pdbpm->Deskbars[i]) { HWND hwndDeskbar; pdbpm->Deskbars[i]->GetWindow(&hwndDeskbar); if (hwndDeskbar == hwnd) { return _GetBandSite(pdbpm->Deskbars[i]); } } } } } return NULL; }
// }
|