|
|
#include "priv.h"
#include "sccls.h"
#include "nscband.h"
#include "resource.h"
#include "uemapp.h" // KMTF: Included for instrumentation
#include "shlguid.h"
#include <dpa.h>
#include <mluisupp.h>
#include "varutil.h"
#include "apithk.h"
#define TF_EXPLORERBAND 0
typedef struct { LPITEMIDLIST pidl; IShellFolder *psf; } SFCITEM;
class CExplorerBand : public CNSCBand, public IDispatch { public:
// *** IUnknown ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void) { return CNSCBand::AddRef(); }; STDMETHODIMP_(ULONG) Release(void) { return CNSCBand::Release(); };
// *** IOleCommandTarget methods ***
STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext); STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);
// *** IDockingWindow methods ***
STDMETHODIMP CloseDW(DWORD dw); STDMETHODIMP ShowDW(BOOL fShow);
// *** IObjectWithSite methods ***
STDMETHODIMP SetSite(IUnknown* punkSite);
// *** INamespaceProxy methods ***
STDMETHODIMP Invoke(LPCITEMIDLIST pidl); STDMETHODIMP OnSelectionChanged(LPCITEMIDLIST pidl); STDMETHODIMP CacheItem(LPCITEMIDLIST pidl) {_MaybeAddToLegacySFC(pidl); return S_OK;} // *** IDispatch methods ***
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) {return E_NOTIMPL;} STDMETHODIMP GetTypeInfo(UINT itinfo,LCID lcid,ITypeInfo **pptinfo) {return E_NOTIMPL;} STDMETHODIMP GetIDsOfNames(REFIID riid,OLECHAR **rgszNames,UINT cNames, LCID lcid, DISPID * rgdispid) {return E_NOTIMPL;} STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr);
protected: CExplorerBand() : _fCanSelect(TRUE), _fIgnoreSelection(TRUE) {} virtual ~CExplorerBand(); virtual HRESULT _TranslatePidl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib); virtual BOOL _ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib); virtual HRESULT _InitializeNsc(); virtual DWORD _GetTVStyle(); virtual DWORD _GetTVExStyle(); virtual DWORD _GetEnumFlags(); void _MaybeAddToLegacySFC(LPCITEMIDLIST pidl); void _AddToLegacySFC(LPCITEMIDLIST pidl, IShellFolder *psf); BOOL _IsInSFC(LPCITEMIDLIST pidl); BOOL _IsFloppy(LPCITEMIDLIST pidl); void _OnNavigate(); HRESULT _ConnectToBrowser(BOOL fConnect); friend HRESULT CExplorerBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi);
static void s_DVEnumReadyCallback(void *pvData);
CDSA<SFCITEM> *_pdsaLegacySFC; DWORD _dwcpCookie; LPITEMIDLIST _pidlView; //pidl view is navigated to
BOOL _fCanSelect; BOOL _fIgnoreSelection; //so we don't navigate away from the web page when user opens explorer pane
BOOL _fFloppyRefresh; };
HRESULT _UnwrapRootedPidl(LPCITEMIDLIST pidlRooted, BOOL bOnlyIfRooted, LPITEMIDLIST *ppidl) { HRESULT hr = E_FAIL; if (ILIsRooted(pidlRooted)) { hr = SHILCombine(ILRootedFindIDList(pidlRooted), _ILNext(pidlRooted), ppidl); } else if (!bOnlyIfRooted) { hr = SHILClone(pidlRooted, ppidl); }
return hr; }
BOOL IsFTPPidl(LPCITEMIDLIST pidl) { BOOL fIsFTP = FALSE; IShellFolder * psf;
if (pidl && SUCCEEDED(IEBindToObject(pidl, &psf))) { fIsFTP = IsFTPFolder(psf); psf->Release(); }
return fIsFTP; }
void CExplorerBand::_OnNavigate() { IBrowserService* pbs; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs)); if (SUCCEEDED(hr)) { LPITEMIDLIST pidl; hr = pbs->GetPidl(&pidl); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlNew; hr = _UnwrapRootedPidl(pidl, FALSE, &pidlNew); if (SUCCEEDED(hr)) { // We must go in this code path if the pidl is an FTP pidl. FTP pidls can contain
// passwords so it needs to replace any existing pidl. Whistler #252206.
if (!_pidlView || !ILIsEqual(pidlNew, _pidlView) || IsFTPPidl(pidlNew)) { DWORD dwAttributes = SFGAO_FOLDER; // only let folders go through (to filter out Web pages)
hr = IEGetAttributesOf(pidlNew, &dwAttributes); if (SUCCEEDED(hr) && (dwAttributes & SFGAO_FOLDER)) { BOOL fExpand = (_pidlView == NULL); //the very first time we expand the folder the view is navigated to
Pidl_Set(&_pidlView, pidlNew); _fIgnoreSelection = FALSE; //in the web page case we don't come here because the page does not have folder attribute
if (_fCanSelect) { if (fExpand) { VARIANT var; hr = InitVariantFromIDList(&var, _pidlView); if (SUCCEEDED(hr)) { IShellNameSpace *psns; hr = _pns->QueryInterface(IID_PPV_ARG(IShellNameSpace, &psns)); if (SUCCEEDED(hr)) { psns->Expand(var, 1); psns->Release(); } VariantClear(&var); } } else { _pns->SetSelectedItem(_pidlView, TRUE, FALSE, 0); } } } } // view navigation is asynchronous so we don't know if it failed in OnSelectionChanged
// but the view is getting navigated to the old pidl and _fCanSelect is false (which happens after we try
// to navigate the view) so it is safe to assume that navigation failed.
// we need to update the selection to match the view
else if (ILIsEqual(pidlNew, _pidlView) && !_fCanSelect) { _pns->SetSelectedItem(_pidlView, TRUE, FALSE, 0); } _fCanSelect = TRUE; ILFree(pidlNew); } ILFree(pidl); } pbs->Release(); }
if (FAILED(hr)) { Pidl_Set(&_pidlView, NULL); } }
HRESULT CExplorerBand::Invoke(DISPID dispidMember, REFIID riid,LCID lcid, WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr) { HRESULT hr = S_OK;
if (!pdispparams) return E_INVALIDARG;
switch(dispidMember) { case DISPID_NAVIGATECOMPLETE2: case DISPID_DOCUMENTCOMPLETE: { BOOL fCallNavigateFinished = TRUE; IDVGetEnum *pdvge; if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_SFolderView, IID_PPV_ARG(IDVGetEnum, &pdvge)))) { // callback will call it
fCallNavigateFinished = FALSE; if (dispidMember == DISPID_NAVIGATECOMPLETE2) pdvge->SetEnumReadyCallback(s_DVEnumReadyCallback, this); pdvge->Release(); } _OnNavigate(); if (fCallNavigateFinished && DISPID_DOCUMENTCOMPLETE == dispidMember) { // need to let nsc know the navigation finished in case we navigated to a 3rd party namespace extension (w/ its own view impl)
// because it does not implement IDVGetEnum, hence s_DVEnumReadyCallback will not get called
LPITEMIDLIST pidlClone = ILClone(_pidlView); // should we unwrap this pidl if rooted?
if (pidlClone) _pns->RightPaneNavigationFinished(pidlClone); // takes ownership
} } break;
default: hr = E_INVALIDARG; break; }
return hr; }
void CExplorerBand::s_DVEnumReadyCallback(void *pvData) { CExplorerBand *peb = (CExplorerBand *) pvData; IBrowserService* pbs; if (SUCCEEDED(IUnknown_QueryService(peb->_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs)))) { LPITEMIDLIST pidlTemp; if (SUCCEEDED(pbs->GetPidl(&pidlTemp))) { LPITEMIDLIST pidl; if (SUCCEEDED(_UnwrapRootedPidl(pidlTemp, FALSE, &pidl))) { peb->_pns->RightPaneNavigationFinished(pidl); // takes ownership
} ILFree(pidlTemp); } pbs->Release(); } }
const TCHAR c_szLink[] = TEXT("link"); const TCHAR c_szRename[] = TEXT("rename"); const TCHAR c_szMove[] = TEXT("cut"); const TCHAR c_szPaste[] = TEXT("paste"); const TCHAR c_szCopy[] = TEXT("copy"); const TCHAR c_szDelete[] = TEXT("delete"); const TCHAR c_szProperties[] = TEXT("properties");
// IOleCommandTarget
HRESULT CExplorerBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { if (pguidCmdGroup == NULL) { IContextMenu *pcm = NULL; HRESULT hr = _QueryContextMenuSelection(&pcm); if (SUCCEEDED(hr)) { HMENU hmenu = CreatePopupMenu(); if (hmenu) { hr = pcm->QueryContextMenu(hmenu, 0, 0, 255, 0); if (SUCCEEDED(hr)) { UINT ilast = GetMenuItemCount(hmenu); for (UINT ipos=0; ipos < ilast; ipos++) { MENUITEMINFO mii = {0}; TCHAR szVerb[40]; UINT idCmd;
mii.cbSize = SIZEOF(MENUITEMINFO); mii.fMask = MIIM_ID|MIIM_STATE;
if (!GetMenuItemInfoWrap(hmenu, ipos, TRUE, &mii)) continue; if (0 != (mii.fState & (MF_GRAYED|MF_DISABLED))) continue; idCmd = mii.wID;
hr = ContextMenu_GetCommandStringVerb(pcm, idCmd, szVerb, ARRAYSIZE(szVerb)); if (SUCCEEDED(hr)) { LPCTSTR szCmd = NULL;
for (ULONG cItem = 0; cItem < cCmds; cItem++) { switch (rgCmds[cItem].cmdID) { case OLECMDID_CUT: szCmd = c_szMove; break; case OLECMDID_COPY: szCmd = c_szCopy; break; case OLECMDID_PASTE: szCmd = c_szPaste; break; case OLECMDID_DELETE: szCmd = c_szDelete; break; case OLECMDID_PROPERTIES: szCmd = c_szProperties; break; } if (StrCmpI(szVerb, szCmd)==0) { rgCmds[cItem].cmdf = OLECMDF_ENABLED; } } } } } DestroyMenu(hmenu); } else { hr = E_FAIL; } pcm->Release(); } if (SUCCEEDED(hr)) return hr; }
return CNSCBand::QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext); }
HRESULT CExplorerBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { if (pguidCmdGroup == NULL) { HRESULT hr; switch(nCmdID) { case OLECMDID_CUT: hr = _InvokeCommandOnItem(c_szMove); break; case OLECMDID_COPY: hr = _InvokeCommandOnItem(c_szCopy); break; case OLECMDID_PASTE: hr = _InvokeCommandOnItem(c_szPaste); break; case OLECMDID_DELETE: hr = _InvokeCommandOnItem(c_szDelete); break; case OLECMDID_PROPERTIES: hr = _InvokeCommandOnItem(c_szProperties); break; default: hr = E_FAIL; break; }
if (SUCCEEDED(hr)) return hr; }
return CNSCBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); }
// IDockingWindow
HRESULT CExplorerBand::CloseDW(DWORD dw) { _ConnectToBrowser(FALSE); return CNSCBand::CloseDW(dw); }
HRESULT CExplorerBand::ShowDW(BOOL fShow) { return CNSCBand::ShowDW(fShow); }
// IObjectWithSite
HRESULT CExplorerBand::SetSite(IUnknown* punkSite) { HRESULT hr = CNSCBand::SetSite(punkSite);
if (punkSite) _ConnectToBrowser(TRUE);
return hr; }
int _SFCDestroyCB(SFCITEM *psfcItem, void *pv) { psfcItem->psf->Release(); ILFree(psfcItem->pidl); return 1; }
CExplorerBand::~CExplorerBand() { ILFree(_pidlView); if (_pdsaLegacySFC) { _pdsaLegacySFC->DestroyCallback(_SFCDestroyCB, NULL); delete _pdsaLegacySFC; } }
HRESULT CExplorerBand::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CExplorerBand, IDispatch), { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppvObj); if (FAILED(hr)) hr = CNSCBand::QueryInterface(riid, ppvObj); return hr; }
DWORD CExplorerBand::_GetEnumFlags() { DWORD dwFlags = SHCONTF_FOLDERS; SHELLSTATE ss = {0}; SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE); if (ss.fShowAllObjects) dwFlags |= SHCONTF_INCLUDEHIDDEN; return dwFlags; }
DWORD CExplorerBand::_GetTVExStyle() { DWORD dwExStyle = 0; if (IsOS(OS_WHISTLERORGREATER) && SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), TEXT("FriendlyTree"), FALSE, TRUE)) { dwExStyle |= TVS_EX_NOSINGLECOLLAPSE; }
return dwExStyle; }
DWORD CExplorerBand::_GetTVStyle() { DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_HSCROLL | TVS_EDITLABELS | TVS_SHOWSELALWAYS;
if (SHRegGetBoolUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), TEXT("FriendlyTree"), FALSE, TRUE)) { dwStyle |= TVS_HASBUTTONS | TVS_SINGLEEXPAND | TVS_TRACKSELECT; } else { dwStyle |= TVS_HASBUTTONS | TVS_HASLINES; }
// If the parent window is mirrored then the treeview window will inheret the mirroring flag
// And we need the reading order to be Left to right, which is the right to left in the mirrored mode.
if (_hwndParent && IS_WINDOW_RTL_MIRRORED(_hwndParent)) { // This means left to right reading order because this window will be mirrored.
_dwStyle |= TVS_RTLREADING; }
return dwStyle; }
HRESULT CExplorerBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi) { // aggregation checking is handled in class factory
CExplorerBand * peb = new CExplorerBand(); if (!peb) return E_OUTOFMEMORY;
if (SUCCEEDED(peb->_Init((LPCITEMIDLIST)CSIDL_DESKTOP))) { peb->_pns = CNscTree_CreateInstance(); if (peb->_pns) { ASSERT(poi); peb->_poi = poi; // if you change this cast, fix up CFavBand_CreateInstance
*ppunk = SAFECAST(peb, IDeskBand *);
IUnknown_SetSite(peb->_pns, *ppunk); peb->_SetNscMode(MODE_NORMAL);
return S_OK; } } peb->Release();
return E_FAIL; }
HRESULT CExplorerBand::_ConnectToBrowser(BOOL fConnect) { IBrowserService* pbs; HRESULT hr = IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs)); if (SUCCEEDED(hr)) { if (fConnect) { LPITEMIDLIST pidlTemp = NULL; // try to get the pidl the browser is navigated to
// this usually fails if user just opened Explorer window because navigation is asynchronous
// so we're not initialized yet
if (FAILED(pbs->GetPidl(&pidlTemp))) { IBrowserService2 *pbs2; if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IBrowserService2, &pbs2)))) { LPCBASEBROWSERDATA pbbd; // our last hope is the pidl browser is navigating to...
if (SUCCEEDED(pbs2->GetBaseBrowserData(&pbbd)) && pbbd->_pidlPending) { pidlTemp = ILClone(pbbd->_pidlPending); } pbs2->Release(); } }
if (pidlTemp) { LPITEMIDLIST pidl; // see if we're dealing with a rooted namespace
if (SUCCEEDED(_UnwrapRootedPidl(pidlTemp, TRUE, &pidl))) { _Init(pidl); //if so, reinitialize ourself with the rooted pidl
ILFree(pidl); } ILFree(pidlTemp); } } IConnectionPointContainer* pcpc; hr = IUnknown_QueryService(pbs, SID_SWebBrowserApp, IID_PPV_ARG(IConnectionPointContainer, &pcpc)); // Let's now have the Browser Window give us notification when something happens.
if (SUCCEEDED(hr)) { hr = ConnectToConnectionPoint(SAFECAST(this, IDispatch*), DIID_DWebBrowserEvents2, fConnect, pcpc, &_dwcpCookie, NULL); pcpc->Release(); }
pbs->Release(); } ASSERT(SUCCEEDED(hr)); return hr; }
HRESULT CExplorerBand::_InitializeNsc() { HRESULT hr = _pns->Initialize(_pidl, _GetEnumFlags(), NSS_DROPTARGET | NSS_BROWSERSELECT); if (SUCCEEDED(hr)) _OnNavigate();
return hr; }
HRESULT CExplorerBand::_TranslatePidl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib) { HRESULT hr = E_INVALIDARG;
if (pidl && ppidlTarget && pulAttrib) { hr = IEGetAttributesOf(pidl, pulAttrib); if (SUCCEEDED(hr)) { hr = SHILClone(pidl, ppidlTarget); } } return hr; }
BOOL CExplorerBand::_ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib) { return ulAttrib & SFGAO_FOLDER; }
BOOL CExplorerBand::_IsFloppy(LPCITEMIDLIST pidl) { BOOL fRet = FALSE;
WCHAR szPath[MAX_PATH]; if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL))) { if (DRIVE_REMOVABLE == GetDriveType(szPath)) { fRet = (L'A' == szPath[0] || L'B' == szPath[0] || L'a' == szPath[0] || L'b' == szPath[0]); } }
return fRet; }
HRESULT CExplorerBand::Invoke(LPCITEMIDLIST pidl) { HRESULT hr;
// allow user to navigate to an already selected item if they opened Explorer band in Web browser
// (because we put selection on the root node but don't navigate away from the web page, if they click
// on the root we don't navigate there, because selection never changed)
if (!_pidlView) { _fIgnoreSelection = FALSE; hr = OnSelectionChanged(pidl); } else if (ILIsEqual(pidl, _pidlView) && _IsFloppy(pidl)) { // If the drive is a floppy and the user reselects the drive refresh the contents. This enables
// a user to refresh when a floppy is replaced.
_fFloppyRefresh = TRUE; hr = OnSelectionChanged(pidl); _fFloppyRefresh = FALSE; } else { hr = S_OK; }
return hr; }
HRESULT CExplorerBand::OnSelectionChanged(LPCITEMIDLIST pidl) { HRESULT hr = E_INVALIDARG;
if (!_fIgnoreSelection) { if (pidl) { ULONG ulAttrib = SFGAO_FOLDER; LPITEMIDLIST pidlTarget;
hr = GetNavigateTarget(pidl, &pidlTarget, &ulAttrib); if (hr == S_OK) { if (!_pidlView || _fFloppyRefresh || !ILIsEqual(pidlTarget, _pidlView)) { hr = CNSCBand::Invoke(pidlTarget); if (SUCCEEDED(hr)) _fCanSelect = FALSE; _pns->RightPaneNavigationStarted(pidlTarget); pidlTarget = NULL; // ownership passed
} ILFree(pidlTarget); } #ifdef DEBUG
else if (hr == S_FALSE) { ASSERT(pidlTarget == NULL); } #endif
} } else { _fIgnoreSelection = FALSE; //we ignore only first selection
} return hr; }
void CExplorerBand::_MaybeAddToLegacySFC(LPCITEMIDLIST pidl) { IShellFolder *psf = NULL; if (pidl && SUCCEEDED(SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IShellFolder, &psf)))) { //
// APPCOMPAT LEGACY - Compatibility. needs the Shell folder cache, - ZekeL - 4-MAY-99
// some apps, specifically WS_FTP and AECO Zip Pro,
// rely on having a shellfolder existing in order for them to work.
// we pulled the SFC because it wasnt any perf win.
//
if (OBJCOMPATF_OTNEEDSSFCACHE & SHGetObjectCompatFlags(psf, NULL)) _AddToLegacySFC(pidl, psf); psf->Release(); } }
BOOL CExplorerBand::_IsInSFC(LPCITEMIDLIST pidl) { BOOL bReturn = FALSE;
ASSERT(_pdsaLegacySFC); for (int i=0; i<_pdsaLegacySFC->GetItemCount(); i++) { SFCITEM *psfcItem = _pdsaLegacySFC->GetItemPtr(i); if (ILIsEqual(psfcItem->pidl, pidl)) { bReturn = TRUE; break; } }
return bReturn; }
void CExplorerBand::_AddToLegacySFC(LPCITEMIDLIST pidl, IShellFolder *psf) { if (!_pdsaLegacySFC) { _pdsaLegacySFC = new CDSA<SFCITEM>; if (_pdsaLegacySFC && !_pdsaLegacySFC->Create(4)) { delete _pdsaLegacySFC; _pdsaLegacySFC = NULL; } }
if (_pdsaLegacySFC) { LPITEMIDLIST pidlCache; if (!_IsInSFC(pidl) && SUCCEEDED(SHILClone(pidl, &pidlCache))) { SFCITEM sfc = {pidlCache, psf}; if (-1 != _pdsaLegacySFC->InsertItem(0, &sfc)) psf->AddRef(); else ILFree(pidlCache); } } }
|