windows-nt/Source/XPSP1/NT/shell/shdocvw/nscband.cpp
2020-09-26 16:20:57 +08:00

717 lines
20 KiB
C++

/**************************************************************\
FILE: NSCBand.cpp
DESCRIPTION: implementation of CNSCBand. the class CNSCBand
exists to support name space control bands. A name
space control uses IShellFolder rooted in various
namespaces including Favorites, history, Shell Name
Space, etc. to depict a hierarchical UI
representation of the given name space.
AUTHOR: chrisny
\**************************************************************/
#include "priv.h"
#include "sccls.h"
#include "util.h"
#include "resource.h"
#include "dhuihand.h"
#include "nscband.h"
#include <varutil.h>
#include <mluisupp.h>
HRESULT CNSCBand::_Init(LPCITEMIDLIST pidl)
{
// further initialization happens in ShowDW
_fInited = FALSE;
_fVisible = FALSE;
_fCanFocus = TRUE;
_haccTree = LoadAccelerators(MLGetHinst(), MAKEINTRESOURCE(ACCEL_FAVBAR));
// pidl can be real or a CSIDL_ constant
if (HIWORD(pidl))
_pidl = ILClone(pidl);
else
SHGetSpecialFolderLocation(NULL, LOWORD(pidl), &_pidl);
return _pidl ? S_OK : E_FAIL;
}
CNSCBand::~CNSCBand()
{
if (_pidl)
ILFree(_pidl);
ATOMICRELEASE(_pns);
ATOMICRELEASE(_pweh);
if (_himlNormal)
ImageList_Destroy(_himlNormal);
if (_himlHot)
ImageList_Destroy(_himlHot);
}
HRESULT CNSCBand::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CNSCBand, IContextMenu), // IID_IContextMenu
QITABENT(CNSCBand, IWinEventHandler), // IID_IWinEventHandler
QITABENT(CNSCBand, IBandNavigate), // IID_IBandNavigate
QITABENT(CNSCBand, INamespaceProxy), // IID_INamespaceProxy
{ 0 },
};
HRESULT hres = QISearch(this, qit, riid, ppvObj);
if (FAILED(hres))
hres = CToolBand::QueryInterface(riid, ppvObj);
return hres;
}
#ifndef ENABLE_CCHANNELBAND
HRESULT CNSCBand_CreateInstanceEx(IUnknown *punkOuter, IUnknown **ppunk,
LPCOBJECTINFO poi, LPCITEMIDLIST pidl)
{
// aggregation checking is handled in class factory
HRESULT hres;
CNSCBand * p = new CNSCBand();
if (p)
{
hres = p->_Init(pidl);
if (SUCCEEDED(hres))
{
p->_pns = CNscTree_CreateInstance();
if (p->_pns)
{
p->_poi = poi;
// if you change this cast, fix up CChannelBand_CreateInstance
*ppunk = SAFECAST(p, IDeskBand *);
IUnknown_SetSite(p->_pns, *ppunk);
hres = S_OK;
}
}
p->Release();
}
else
hres = E_OUTOFMEMORY;
return hres;
}
#endif
#ifdef ENABLE_CHANNELS
extern LPITEMIDLIST Channel_GetFolderPidl();
HRESULT CChannelBand_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
#ifndef ENABLE_CCHANNELBAND
ASSERT(FALSE);
return E_FAIL;
#else
HRESULT hres = CNSCBand_CreateInstanceEx(punkOuter, ppunk, poi, Channel_GetFolderPidl());
if (*ppunk) {
CNSCBand* p = (CNSCBand*)(IDeskBand*)*ppunk;
p->_SetNscMode(MODE_CHANNELS);
}
return hres;
#endif
}
#endif // ENABLE_CHANNELS
extern HRESULT GetHistoryPIDL(LPITEMIDLIST *ppidlHistory);
HRESULT CNSCBand::CloseDW(DWORD dw)
{
if (_fVisible)
{
_UnregisterBand();
}
if (_pns)
{
IUnknown_SetSite(_pns, NULL); // Break the ref-count cycle.
}
return CToolBand::CloseDW(dw);
}
void CNSCBand::_UnregisterBand()
{
IBrowserService *pswProxy;
QueryService(SID_SProxyBrowser, IID_IBrowserService, (LPVOID*)&pswProxy);
ASSERT(pswProxy);
if (pswProxy)
{
IOleCommandTarget *poctProxy;
if (SUCCEEDED(pswProxy->QueryInterface(IID_IOleCommandTarget, (void **)&poctProxy)))
{
VARIANT var;
VariantInit(&var);
// Register ourselves for SBCMDID_SELECTHISTPIDL,SBCMDID_INITFILECTXMENU
var.vt = VT_UNKNOWN;
QueryInterface(IID_IUnknown, (void **)&var.punkVal);
poctProxy->Exec(&CGID_Explorer, SBCMDID_UNREGISTERNSCBAND, OLECMDEXECOPT_PROMPTUSER, &var, NULL);
VariantClear(&var);
poctProxy->Release();
}
pswProxy->Release();
}
}
HRESULT CNSCBand::_InitializeNsc()
{
return _pns->Initialize(_pidl, _GetEnumFlags(), NSS_DROPTARGET | NSS_BROWSERSELECT);
}
HRESULT CNSCBand::ShowDW(BOOL fShow)
{
BOOL fIsHistory = IsEqualCLSID(*_poi->pclsid, CLSID_HistBand);
if (fShow && _hwnd && !_fVisible)
{
IBrowserService *pswProxy;
QueryService(SID_SProxyBrowser, IID_PPV_ARG(IBrowserService, &pswProxy));
ASSERT(pswProxy);
if (!_fInited)
{
_InitializeNsc();
}
else
{
_pns->ShowWindow(TRUE);
}
if (pswProxy)
{
IOleCommandTarget *poctProxy;
if (SUCCEEDED(pswProxy->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &poctProxy))))
{
VARIANT var;
VariantInit(&var);
// Register ourselves for SBCMDID_SELECTHISTPIDL,SBCMDID_INITFILECTXMENU
var.vt = VT_UNKNOWN;
QueryInterface(IID_PPV_ARG(IUnknown, &var.punkVal));
poctProxy->Exec(&CGID_Explorer, SBCMDID_REGISTERNSCBAND, OLECMDEXECOPT_PROMPTUSER, &var, NULL);
//clear the variant cheaply
var.vt = VT_EMPTY;
Release();
// do any special registration if necessary
_OnRegisterBand(poctProxy);
poctProxy->Release();
}
pswProxy->Release();
}
_fInited = TRUE;
_fVisible = TRUE;
}
else if (!fShow && _fVisible)
{
_pns->ShowWindow(FALSE);
_UnregisterBand();
_fVisible = FALSE;
}
return CToolBand::ShowDW(fShow);
}
HRESULT CNSCBand::GetWindow(HWND *phwnd)
{
INSCTree2 *pns2;
HRESULT hr = _pns->QueryInterface(IID_PPV_ARG(INSCTree2, &pns2));
if (SUCCEEDED(hr))
{
pns2->CreateTree2(_hwndParent, _GetTVStyle(), _GetTVExStyle(), &_hwnd);
hr = CToolBand::GetWindow(phwnd);
pns2->Release();
}
return hr;
}
DWORD CNSCBand::_GetTVStyle()
{
DWORD dwFlags = TVS_FULLROWSELECT | TVS_TRACKSELECT | TVS_INFOTIP;
DWORD dwValue;
DWORD dwSize = SIZEOF(dwValue);
BOOL fDefault = TRUE;
SHRegGetUSValue(L"Software\\Microsoft\\Internet Explorer\\Main",
L"NscSingleExpand", NULL, (LPBYTE)&dwValue, &dwSize, FALSE,
(void *) &fDefault, SIZEOF(fDefault));
if (dwValue)
dwFlags |= TVS_SINGLEEXPAND;
return dwFlags;
}
HRESULT CNSCBand::GetBandInfo(DWORD dwBandID, DWORD fViewMode,
DESKBANDINFO* pdbi)
{
_dwBandID = dwBandID;
pdbi->dwModeFlags = DBIMF_FIXEDBMP | DBIMF_VARIABLEHEIGHT;
pdbi->ptMinSize.x = 16;
pdbi->ptMinSize.y = 0;
pdbi->ptMaxSize.x = 32000; // random
pdbi->ptMaxSize.y = 32000; // random
pdbi->ptActual.y = -1;
pdbi->ptActual.x = -1;
pdbi->ptIntegral.y = 1;
if (_szTitle[0])
{
StrCpyNW(pdbi->wszTitle, _szTitle, ARRAYSIZE(pdbi->wszTitle));
}
else
{
CLSID clsid;
UINT ids;
GetClassID(&clsid);
if (IsEqualIID(clsid, CLSID_FavBand))
ids = IDS_BAND_FAVORITES;
else if (IsEqualIID(clsid, CLSID_HistBand))
ids = IDS_BAND_HISTORY;
else if (IsEqualIID(clsid, CLSID_ExplorerBand))
ids = IDS_BAND_EXPLORER;
else
{
ASSERT(FALSE); // BOGUS BAND!!!
return S_FALSE;
}
MLLoadStringW(ids, pdbi->wszTitle, ARRAYSIZE(pdbi->wszTitle));
}
return S_OK;
}
void _InitColors(BOOL fReinit);
// *** IWinEventHandler methods ***
HRESULT CNSCBand::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres)
{
HRESULT hr = E_FAIL;
if (!_pweh && _pns)
_pns->QueryInterface(IID_IWinEventHandler, (void **) &_pweh);
// We need to tell the bandsite that we have become active if we're getting a
// click focus or something
if (uMsg == WM_NOTIFY && ((LPNMHDR)lParam)->code == NM_SETFOCUS)
{
IUnknown_OnFocusChangeIS(_punkSite, SAFECAST(this, IInputObject*), TRUE);
}
if (_pweh)
hr = _pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, plres);
return hr;
}
HRESULT CNSCBand::IsWindowOwner(HWND hwnd)
{
HRESULT hres;
hres = SHIsChildOrSelf(_hwnd, hwnd);
ASSERT(hwnd != NULL || hres == S_FALSE);
ASSERT(_hwnd != NULL || hres == S_FALSE);
return hres;
}
//*** CNSCBand::IPersistStream::* {
HRESULT CNSCBand::GetClassID(CLSID *pClassID)
{
ASSERT(_poi->pclsid != NULL);
*pClassID = *(_poi->pclsid);
return S_OK;
}
HRESULT CNSCBand::Load(IStream *pstm)
{
return S_OK;
}
HRESULT CNSCBand::Save(IStream *pstm, BOOL fClearDirty)
{
return S_OK;
}
// }
//*** CNSCBand::IContextMenu::* {
HRESULT CNSCBand::QueryContextMenu(HMENU hmenu,
UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
int i = 0;
#if 0
HMENU hmenuMe = LoadMenuPopup(MENU_IWBBAND);
i += Shell_MergeMenus(hmenu, hmenuMe, indexMenu, idCmdFirst + i, idCmdLast, MM_ADDSEPARATOR) - (idCmdFirst + i);
DestroyMenu(hmenuMe);
#endif
// aka (S_OK|i)
return MAKE_HRESULT(ERROR_SUCCESS, FACILITY_NULL, i);
}
HRESULT CNSCBand::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
#if 0
int idCmd = -1;
if (!HIWORD(pici->lpVerb))
idCmd = LOWORD(pici->lpVerb);
switch (idCmd)
{
case default:
TraceMsg(DM_ERROR, "cbb::ic cmd=%d not handled", idCmd);
break;
}
#endif
return S_OK;
}
HRESULT CNSCBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
for (UINT i=0; i < cCmds; i++)
{
rgCmds[i].cmdf = 0;
switch (rgCmds[i].cmdID)
{
case SBCMDID_INITFILECTXMENU:
if (_hwnd && _fVisible)
{
rgCmds->cmdf = 0;
if (pcmdtext)
pcmdtext->cmdtextf = 0;
if (pcmdtext)
{
if (SUCCEEDED(_pns->GetSelectedItemName(pcmdtext->rgwz, pcmdtext->cwBuf)))
{
rgCmds->cmdf = OLECMDF_ENABLED;
pcmdtext->cmdtextf = OLECMDTEXTF_NAME;
pcmdtext->cwActual = lstrlenW(pcmdtext->rgwz) + 1;
}
}
}
break;
case SBCMDID_FILERENAME:
case SBCMDID_FILEDELETE:
case SBCMDID_FILEPROPERTIES:
{
LPITEMIDLIST pidl;
// get selected item can return NULL pidl and S_FALSE
if (_pns->GetSelectedItem(&pidl, 0) == S_OK)
{
DWORD rgfAttrib = SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET; // CAN_LINK
if (SUCCEEDED(IEGetAttributesOf(pidl, &rgfAttrib)))
{
DWORD nCmdID;
static const DWORD tbtab[] = {
SBCMDID_FILEDELETE, SBCMDID_FILEPROPERTIES, SBCMDID_FILERENAME };
static const DWORD cttab[] = {
SFGAO_CANDELETE, SFGAO_HASPROPSHEET, SFGAO_CANRENAME };
nCmdID = SHSearchMapInt((int*)tbtab, (int*)cttab, ARRAYSIZE(tbtab), rgCmds[i].cmdID);
if (nCmdID != -1 && (rgfAttrib & nCmdID))
rgCmds[i].cmdf = OLECMDF_ENABLED;
}
ILFree(pidl);
}
break;
}
default:
break;
}
}
return S_OK;
}
return CToolBand::QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
}
HRESULT CNSCBand::_InvokeCommandOnItem(LPCTSTR pszVerb)
{
HRESULT hr;
IContextMenu *pcm;
hr = _QueryContextMenuSelection(&pcm);
if (SUCCEEDED(hr))
{
CMINVOKECOMMANDINFOEX ici =
{
SIZEOF(CMINVOKECOMMANDINFOEX),
0L,
_hwnd,
NULL,
NULL, NULL,
SW_NORMAL,
};
#ifdef UNICODE
CHAR szVerbAnsi[MAX_PATH];
SHUnicodeToAnsi(pszVerb, szVerbAnsi, ARRAYSIZE(szVerbAnsi));
ici.lpVerb = szVerbAnsi;
ici.lpVerbW = pszVerb;
ici.fMask |= CMIC_MASK_UNICODE;
#else
ici.lpVerb = pszVerb;
#endif
hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
pcm->Release();
}
return hr;
}
HRESULT CNSCBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
if (pguidCmdGroup == NULL)
{
switch (nCmdID)
{
case OLECMDID_REFRESH:
if (_pns)
_pns->Refresh();
return S_OK;
}
}
else if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
HRESULT hr = S_OK;
switch (nCmdID)
{
case SBCMDID_SELECTHISTPIDL:
if (IsEqualCLSID(*_poi->pclsid, CLSID_HistBand) && _hwnd && _fVisible)
{
// If you're not visible do nothing. On becoming visible
// use Exec to proxy to get last pidlSelect that you would
// have shown, had you been visible
LPITEMIDLIST pidlSelect = VariantToIDList(pvarargIn);
if (pidlSelect)
{
_pns->SetSelectedItem(pidlSelect, TRUE, FALSE, 0);
ILFree(pidlSelect);
}
}
break;
case SBCMDID_INITFILECTXMENU:
if (_hwnd && _fVisible)
{
if (pvarargOut)
{
VariantClearLazy(pvarargOut);
HRESULT hres = _QueryContextMenuSelection((IContextMenu **)&(pvarargOut->punkVal));
if (SUCCEEDED(hres))
{
pvarargOut->vt = VT_UNKNOWN;
}
}
}
break;
case SBCMDID_FILERENAME:
{
IShellNameSpace *psfns;
hr = _pns->QueryInterface(IID_PPV_ARG(IShellNameSpace, &psfns));
if (SUCCEEDED(hr))
{
hr = psfns->InvokeContextMenuCommand(L"rename");
psfns->Release();
}
break;
}
case SBCMDID_FILEDELETE:
hr = _InvokeCommandOnItem(TEXT("delete"));
break;
case SBCMDID_FILEPROPERTIES:
hr = _InvokeCommandOnItem(TEXT("properties"));
break;
default:
hr = E_FAIL;
break;
}
if (SUCCEEDED(hr))
return hr;
}
return CToolBand::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
HRESULT CNSCBand::_QueryContextMenuSelection(IContextMenu ** ppcm)
{
HRESULT hr = E_FAIL;
LPITEMIDLIST pidlSelected;
*ppcm = NULL;
hr = _pns->GetSelectedItem(&pidlSelected, 0);
if (SUCCEEDED(hr))
{
LPCITEMIDLIST pidlRelative;
IShellFolder * psf;
hr = IEBindToParentFolder(pidlSelected, &psf, &pidlRelative);
if (SUCCEEDED(hr))
{
hr = psf->GetUIObjectOf(NULL, 1, &pidlRelative, IID_PPV_ARG_NULL(IContextMenu, ppcm));
}
ILFree(pidlSelected);
}
return hr;
}
HRESULT CNSCBand::Select(LPCITEMIDLIST pidl)
{
_pns->SetSelectedItem(pidl, TRUE, FALSE, 0);
return S_OK;
}
// *** IInputObject Methods ***
HRESULT CNSCBand::TranslateAcceleratorIO(LPMSG lpMsg)
{
HWND hwndFocus = GetFocus();
if (_pns->InLabelEdit())
return EditBox_TranslateAcceleratorST(lpMsg);
else if ( lpMsg && lpMsg->hwnd && SendMessage(lpMsg->hwnd, TVM_TRANSLATEACCELERATOR, 0, (LPARAM)lpMsg))
return S_OK;
else if (hwndFocus == _hwnd && TranslateAcceleratorWrap(_hwnd, _haccTree, lpMsg))
return S_OK;
return S_FALSE;
}
void CNSCBand::_EnsureImageListsLoaded()
{
if (_himlNormal == NULL)
_himlNormal = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDB_HISTORYANDFAVBANDSDEF), 18, 3, RGB(255, 0, 255), IMAGE_BITMAP, LR_CREATEDIBSECTION);
if (_himlHot == NULL)
_himlHot = ImageList_LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDB_HISTORYANDFAVBANDSHOT), 18, 3, RGB(255, 0, 255), IMAGE_BITMAP, LR_CREATEDIBSECTION);
}
HRESULT CNSCBand::_TranslatePidl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib)
{
IShellFolder *psf;
LPCITEMIDLIST pidlLast;
HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
if (SUCCEEDED(hr))
{
hr = SHGetNavigateTarget(psf, pidlLast, ppidlTarget, pulAttrib);
psf->Release();
}
return hr;
}
// favorites, history and Explorer band should override this (they need not worry about channel band)
BOOL CNSCBand::_ShouldNavigateToPidl(LPCITEMIDLIST pidl, ULONG ulAttrib)
{
BOOL bReturn = (ulAttrib & SFGAO_FOLDER);
if (bReturn)
{
IShellFolder *psf;
LPCITEMIDLIST pidlLast;
HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
if (SUCCEEDED(hr))
{
bReturn = IsExpandableChannelFolder(psf, pidlLast);
psf->Release();
}
}
return !bReturn;
}
HRESULT CNSCBand::GetNavigateTarget(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlTarget, ULONG *pulAttrib)
{
HRESULT hr = _TranslatePidl(pidl, ppidlTarget, pulAttrib);
if (SUCCEEDED(hr))
{
hr = _ShouldNavigateToPidl(pidl, *pulAttrib) ? S_OK : S_FALSE;
if (hr == S_FALSE)
{
ILFree(*ppidlTarget);
*ppidlTarget = NULL;
}
}
return hr;
}
HRESULT CNSCBand::OnSelectionChanged(LPCITEMIDLIST pidl)
{
return S_OK;
}
HRESULT CNSCBand::Invoke(LPCITEMIDLIST pidl)
{
HRESULT hr = E_INVALIDARG;
if (pidl)
{
IShellBrowser *psb;
hr = IUnknown_QueryService(_punkSite, SID_SProxyBrowser, IID_PPV_ARG(IShellBrowser, &psb));
if (SUCCEEDED(hr))
{
hr = _NavigateRightPane(psb, pidl);
if (FAILED(hr))
{
IShellFolder *psf;
LPCITEMIDLIST pidlChild;
if (SUCCEEDED(SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild)))
{
DWORD dwAttributes = SFGAO_FOLDER;
psf->GetAttributesOf(1, &pidlChild, &dwAttributes);
if (!(dwAttributes & SFGAO_FOLDER))
{
hr = SHInvokeDefaultCommand(_hwnd, psf, pidlChild);
}
psf->Release();
}
}
psb->Release();
}
}
return hr;
}
HRESULT CNSCBand::_NavigateRightPane(IShellBrowser *psb, LPCITEMIDLIST pidl)
{
HRESULT hr = psb->BrowseObject(pidl, SBSP_SAMEBROWSER);
if (SUCCEEDED(hr))
UEMFireEvent(&UEMIID_BROWSER, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_NAVIGATE, UIBL_NAVOTHER);
return hr;
}