/**************************************************************\ 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 #include 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; }