#include "shellprv.h" #include "common.h" #include "sftbar.h" #include "resource.h" #include "dpastuff.h" #include "shlwapi.h" #include "cobjsafe.h" #include #include "uemapp.h" #include "util.h" #include "brutil.h" #include "dobjutil.h" #include "idlcomm.h" extern UINT g_idFSNotify; #define TF_SFTBAR TF_MENUBAND #define PGMP_RECALCSIZE 200 CSFToolbar::CSFToolbar() { #ifdef CASCADE_DEBUG _fCascadeFolder = TRUE; #endif _dwStyle = TBSTYLE_TOOLTIPS; _fDirty = TRUE; // we havn't enumerated, so our state is dirty _fRegisterChangeNotify = TRUE; _fAllowReorder = TRUE; _tbim.iButton = -1; _iDragSource = -1; _lEvents = SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED| SHCNE_DELETE|SHCNE_RMDIR|SHCNE_RENAMEITEM|SHCNE_RENAMEFOLDER| SHCNE_MEDIAINSERTED|SHCNE_MEDIAREMOVED|SHCNE_NETUNSHARE|SHCNE_NETSHARE| SHCNE_UPDATEITEM|SHCNE_UPDATEIMAGE|SHCNE_ASSOCCHANGED| SHCNE_UPDATEDIR|SHCNE_EXTENDED_EVENT; #define SHCNE_PIDL1ISCHILD \ (SHCNE_DRIVEADD|SHCNE_CREATE|SHCNE_MKDIR|SHCNE_DRIVEREMOVED|\ SHCNE_DELETE|SHCNE_RMDIR|SHCNE_NETUNSHARE|SHCNE_NETSHARE|\ SHCNE_UPDATEITEM) } CSFToolbar::~CSFToolbar() { ATOMICRELEASE(_pcmSF); ATOMICRELEASE(_piml); _ReleaseShellFolder(); ILFree(_pidl); ASSERT(NULL == _hdpa); if (_hwndWorkerWindow) DestroyWindow(_hwndWorkerWindow); OrderList_Destroy(&_hdpaOrder); } HRESULT CSFToolbar::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CSFToolbar, IWinEventHandler), QITABENT(CSFToolbar, IShellChangeNotify), QITABENT(CSFToolbar, IDropTarget), QITABENT(CSFToolbar, IContextMenu), QITABENT(CSFToolbar, IShellFolderBand), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } HRESULT CSFToolbar::SetShellFolder(IShellFolder* psf, LPCITEMIDLIST pidl) { HRESULT hr = E_INVALIDARG; // Save the old values LPITEMIDLIST pidlSave = _pidl; IShellFolder *psfSave = _psf; ITranslateShellChangeNotify *ptscnSave = _ptscn; _psf = NULL; _pidl = NULL; _ptscn = NULL; ASSERT(NULL == psf || IS_VALID_CODE_PTR(psf, IShellFolder)); ASSERT(NULL == pidl || IS_VALID_PIDL(pidl)); if (psf || pidl) { if (psf) { _psf = psf; _psf->AddRef(); _psf->QueryInterface(IID_PPV_ARG(ITranslateShellChangeNotify, &_ptscn)); } if (pidl) _pidl = ILClone(pidl); hr = S_OK; } if (SUCCEEDED(hr)) { ILFree(pidlSave); if (psfSave) psfSave->Release(); if (ptscnSave) ptscnSave->Release(); } else { ASSERT(_psf == NULL); ASSERT(_pidl == NULL); ASSERT(_ptscn == NULL); // we failed -- restore the old values _psf = psfSave; _pidl = pidlSave; _ptscn = ptscnSave; } // This code is here for ShellFolderToolbar reuse. When setting a new shell folder // into an existing band, we will refresh. Note that this is a noop on a new band. _RememberOrder(); _SetDirty(TRUE); if (_fShow) _FillToolbar(); return hr; } HWND CSFToolbar::_CreatePager(HWND hwndParent) { if (!_fMulticolumn) { _hwndPager = CreateWindowEx(0, WC_PAGESCROLLER, NULL, WS_CHILD | WS_TABSTOP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndParent, (HMENU) 0, HINST_THISDLL, NULL); if (_hwndPager) { hwndParent = _hwndPager; } } return hwndParent; } HRESULT CSFToolbar::_CreateToolbar(HWND hwndParent) { if (!_hwndTB) { hwndParent = _CreatePager(hwndParent); _hwndTB = CreateWindowEx(WS_EX_TOOLWINDOW, TOOLBARCLASSNAME, NULL, WS_VISIBLE | WS_CHILD | TBSTYLE_FLAT | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | _dwStyle, 0, 0, 0, 0, hwndParent, (HMENU) 0, HINST_THISDLL, NULL); if (!_hwndTB) { TraceMsg(TF_ERROR, "_hwndTB failed"); return HRESULT_FROM_WIN32(GetLastError()); } if (_hwndPager) SendMessage(_hwndPager, PGM_SETCHILD, 0, (LPARAM)_hwndTB); SendMessage(_hwndTB, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0); // Set the format to ANSI or UNICODE as appropriate. ToolBar_SetUnicodeFormat(_hwndTB, DLL_IS_UNICODE); if (_hwndPager) { // Set the format to ANSI or UNICODE as appropriate. ToolBar_SetUnicodeFormat(_hwndPager, DLL_IS_UNICODE); } // Make sure we're on the same wavelength. SendMessage(_hwndTB, CCM_SETVERSION, COMCTL32_VERSION, 0); SendMessage(_hwndTB, TB_SETEXTENDEDSTYLE, TBSTYLE_EX_DOUBLEBUFFER, TBSTYLE_EX_DOUBLEBUFFER); RECT rc; SIZE size; SystemParametersInfoA(SPI_GETWORKAREA, sizeof(RECT), &rc, FALSE); if (!_hwndPager) { size.cx = RECTWIDTH(rc); size.cy = GetSystemMetrics(SM_CYSCREEN) - (2 * GetSystemMetrics(SM_CYEDGE)); // Need to subrtact off the borders } else { //HACKHACK: THIS WILL FORCE NO WRAP TO HAPPEN FOR PROPER WIDTH CALC WHEN PAGER IS PRESENT. size.cx = RECTWIDTH(rc); size.cy = 32000; } ToolBar_SetBoundingSize(_hwndTB, &size); if (!_SubclassWindow(_hwndTB)) { _fRegisterChangeNotify = FALSE; } } else { if (_hwndPager && GetParent(_hwndPager) != hwndParent) SetParent(_hwndPager, hwndParent); } if (FAILED(_GetTopBrowserWindow(&_hwndDD))) _hwndDD = GetParent(_hwndTB); return S_OK; } #define MAX_COMMANDID 0xFFFF // We're allowed one word of command ids (tested at 5) int CSFToolbar::_GetCommandID() { int id = -1; if (!_fCheckIds) { id = _nNextCommandID++; } else { // We are reusing command ids and must verify that // the current one is not in use. This is slow, but // I assume the number of buttons on one of these // bands is relatively few. // for (int i = 0 ; i <= MAX_COMMANDID ; i++) { TBBUTTONINFO tbbiDummy; tbbiDummy.cbSize = sizeof(tbbiDummy); tbbiDummy.dwMask = 0; // we don't care about data, just existence if (-1 != ToolBar_GetButtonInfo(_hwndTB, _nNextCommandID, &tbbiDummy)) { // A button by this id wasn't found, so the id must be free // id = _nNextCommandID++; break; } _nNextCommandID++; _nNextCommandID %= MAX_COMMANDID; } } if (_nNextCommandID > MAX_COMMANDID) { _nNextCommandID = 0; _fCheckIds = TRUE; } return id; } /*---------------------------------------------------------- Purpose: This function determines the toolbar button style for the given pidl. Returns S_OK if pdwMIFFlags is also set (i.e., the object supported IMenuBandItem to provide more info). S_FALSE if only *pdwTBStyle is set. */ HRESULT CSFToolbar::_TBStyleForPidl(LPCITEMIDLIST pidl, DWORD *pdwTBStyle, DWORD *pdwTBState, DWORD *pdwMIFFlags, int* piIcon) { DWORD dwStyle = TBSTYLE_BUTTON; if (!_fAccelerators) dwStyle |= TBSTYLE_NOPREFIX; *pdwMIFFlags = 0; *pdwTBStyle = dwStyle; *piIcon = -1; *pdwTBState = TBSTATE_ENABLED; return S_FALSE; } PIBDATA CSFToolbar::_CreateItemData(PORDERITEM poi) { return new IBDATA(poi); } // // poiOrIndex can be... // // A valid pointer (which will be treated as a PORDERITEM) // A MAKEINTRESOURCE value (which will be treated as a sentinel value) // PIBDATA CSFToolbar::_AddOrderItemTB(PORDERITEM poiOrIndex, int index, TBBUTTON* ptbb) { TCHAR szName[MAX_PATH]; LPCITEMIDLIST pidlOI; PORDERITEM poi; if (IS_INTRESOURCE(poiOrIndex)) { poi = NULL; pidlOI = (LPCITEMIDLIST)poiOrIndex; } else { poi = poiOrIndex; pidlOI = poi->pidl; } // We need to do this even for NULL because _ObtainPIDLName cooks // up the word "(Empty)" as necessary. _ObtainPIDLName(pidlOI, szName, SIZECHARS(szName)); TBBUTTON tbb = {0}; DWORD dwMIFFlags; DWORD dwStyle; DWORD dwState; int iIcon; int iCommandID = _GetCommandID(); BOOL bNoIcon = FALSE; if (!ptbb) ptbb = &tbb; if (S_OK == _TBStyleForPidl(pidlOI, &dwStyle, &dwState, &dwMIFFlags,&iIcon) && !(dwMIFFlags & SMIF_ICON)) { bNoIcon = TRUE; } PIBDATA pibdata = _CreateItemData(poi); if (pibdata) { pibdata->SetFlags(dwMIFFlags); pibdata->SetNoIcon(bNoIcon); if (!bNoIcon && iIcon != -1) ptbb->iBitmap = iIcon; else ptbb->iBitmap = I_IMAGECALLBACK; ptbb->idCommand = iCommandID; ptbb->fsState = (BYTE)dwState; ptbb->fsStyle = (BYTE)dwStyle; ptbb->dwData = (DWORD_PTR)pibdata; ptbb->iString = (INT_PTR)szName; // Disregard variablewidth if we are vertical if (_fVariableWidth && !_fVertical) ptbb->fsStyle |= TBSTYLE_AUTOSIZE; if (ptbb->idCommand != -1) { if (SendMessage(_hwndTB, TB_INSERTBUTTON, index, (LPARAM)ptbb)) { TraceMsg(TF_BAND, "SFToolbar::_AddPidl %d 0x%x [%s]", ptbb->idCommand, ptbb->dwData, ptbb->iString); } else { delete pibdata; pibdata = NULL; } } } return pibdata; } void CSFToolbar::_ObtainPIDLName(LPCITEMIDLIST pidl, LPTSTR psz, int cchMax) { DisplayNameOf(_psf, pidl, SHGDN_NORMAL, psz, cchMax); } int CSFToolbar::_GetBitmap(int iCommandID, PIBDATA pibdata, BOOL fUseCache) { int iBitmap; if (_fNoIcons || !pibdata || pibdata->GetNoIcon()) { iBitmap = -1; } else { iBitmap = OrderItem_GetSystemImageListIndex(pibdata->GetOrderItem(), _psf, fUseCache); } return iBitmap; } void CSFToolbar::_OnGetDispInfo(LPNMHDR pnm, BOOL fUnicode) { LPNMTBDISPINFO pdi = (LPNMTBDISPINFO)pnm; PIBDATA pibdata = (PIBDATA)pdi->lParam; if (pdi->dwMask & TBNF_IMAGE) { pdi->iImage = _GetBitmap(pdi->idCommand, pibdata, TRUE); } if (pdi->dwMask & TBNF_TEXT) { if (pdi->pszText) { if (fUnicode) { pdi->pszText[0] = TEXT('\0'); } else { pdi->pszText[0] = 0; } } } pdi->dwMask |= TBNF_DI_SETITEM; } // Adds pidl as a new button, handles ILFree(pidl) for the caller // BOOL CSFToolbar::_AddPidl(LPITEMIDLIST pidl, DWORD dwFlags, int index) { if (_hdpa) { PORDERITEM poi = OrderItem_Create(pidl, index); if (poi) { int iPos = DPA_InsertPtr(_hdpa, index, poi); if (-1 != iPos) { // If we did not load an order, then new items should // show up alphabetically in the list, not at the bottom. if (!_fHasOrder && !(dwFlags & FSNA_BULKADD)) { // Sort by name _SortDPA(_hdpa); // Find the index of the order item. We use this index as // the toolbar insert index. index = DPA_GetPtrIndex(_hdpa, poi); } if (_AddOrderItemTB(poi, index, NULL)) { return TRUE; } DPA_DeletePtr(_hdpa, iPos); } OrderItem_Free(poi); return FALSE; } } ILFree(pidl); return FALSE; } BOOL CSFToolbar::_FilterPidl(LPCITEMIDLIST pidl) { return FALSE; } void CSFToolbar::s_NewItem(void *pvParam, LPCITEMIDLIST pidl) { CSFToolbar* psft = (CSFToolbar*)pvParam; psft->v_NewItem(pidl); } HRESULT CSFToolbar::_GetIEnumIDList(DWORD dwEnumFlags, IEnumIDList **ppenum) { ASSERT(_psf); // Pass in a NULL hwnd so the enumerator does not show any UI while // we're filling a band. return IShellFolder_EnumObjects(_psf, NULL, dwEnumFlags, ppenum); } void CSFToolbar::_FillDPA(HDPA hdpa, HDPA hdpaSort, DWORD dwEnumFlags) { IEnumIDList* penum; int cItems = 0; if (!_psf) return; if (S_OK == _GetIEnumIDList(dwEnumFlags, &penum)) { LPITEMIDLIST pidl; ULONG ul; while (S_OK == penum->Next(1, &pidl, &ul)) { cItems++; if (_FilterPidl(pidl) || !OrderList_Append(hdpa, pidl, -1)) { TraceMsg(TF_MENUBAND, "SFToolbar (0x%x)::_FillDPA : Did not Add Pidl (0x%x).", this, pidl); ILFree(pidl); } } penum->Release(); } ORDERINFO oinfo; int iInsertIndex = _tbim.iButton + 1; // This is the button where the cursor sat. // So, We want to insert after that if (iInsertIndex >= ToolBar_ButtonCount(_hwndTB)) // But, if it's at the end, iInsertIndex = -1; // Convert the insert to an append. // - Comments in rhyme by lamadio oinfo.psf = _psf; (oinfo.psf)->AddRef(); oinfo.dwSortBy = (_fHasOrder || _fDropping)? ((_fNoNameSort ? OI_SORTBYORDINAL : OI_SORTBYNAME)): OI_MERGEBYNAME; OrderList_Merge(hdpa, hdpaSort, _fDropping ? iInsertIndex : _DefaultInsertIndex(), (LPARAM) &oinfo, s_NewItem, (void *)this); ATOMICRELEASE(oinfo.psf); } // This function re-enumerates the IShellFolder, keeping things ordered correctly. // void CSFToolbar::_FillToolbar() { HDPA hdpaSort; HDPA hdpa; if (!_fDirty || !_psf) return; // If we have an order array, use that, otherwise // use the currently viewed items // remove the ref for the member variable since we can get reentered // this would be better with real refcounting but this'll do BOOL fTakeOrderRef = FALSE; if (_hdpaOrder) { hdpaSort = _hdpaOrder; // already sorted by name // we set it to null, so we have complete ownership of it. // at the end we're going to nuke _hdpaOrder anyway in _RememberOrder. _hdpaOrder = NULL; fTakeOrderRef = TRUE; } else { hdpaSort = _hdpa; _SortDPA(hdpaSort); } hdpa = DPA_Create(hdpaSort ? DPA_GetPtrCount(hdpaSort) : 12); if (hdpa) { _FillDPA(hdpa, hdpaSort, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS); HDPA hdpaToRemove = DPA_Create(4); if (hdpaToRemove) { HDPA hdpaToAdd = DPA_Create(4); if (hdpaToAdd) { int i, j; BOOL fReleaseAdd = TRUE; if (_hdpa) { // if there isn't anything in the hdpaSort list (which is all the stuff that's already there), // add a null element. this is so it'll generate a "remove this null element" later. // otherwise, we might end up with an (Empty) item left over. if (DPA_GetPtrCount(hdpaSort) == 0) { OrderList_Append(hdpaSort, NULL, 0); } ORDERINFO oi = {0}; oi.dwSortBy = OI_SORTBYNAME; oi.psf = _psf; _psf->AddRef(); DPA_Sort(hdpaSort, OrderItem_Compare, (LPARAM) &oi); DPA_Sort(hdpa, OrderItem_Compare, (LPARAM) &oi); i = 0; j = 0; while ((i < DPA_GetPtrCount(hdpaSort)) && (j < DPA_GetPtrCount(hdpa))) { void *pv1 = DPA_FastGetPtr(hdpaSort, i); void *pv2 = DPA_FastGetPtr(hdpa, j); int nCmp = OrderItem_Compare(pv1, pv2, (LPARAM) &oi); if (nCmp > 0) { DPA_AppendPtr(hdpaToAdd, pv2); j++; } else if (nCmp < 0) { DPA_AppendPtr(hdpaToRemove, pv1); i++; } else { i++; j++; } } while (i < DPA_GetPtrCount(hdpaSort)) { DPA_AppendPtr(hdpaToRemove, DPA_FastGetPtr(hdpaSort, i)); i++; } while (j < DPA_GetPtrCount(hdpa)) { DPA_AppendPtr(hdpaToAdd, DPA_FastGetPtr(hdpa, j)); j++; } _psf->Release(); } else { DPA_Destroy(hdpaToAdd); hdpaToAdd = hdpa; fReleaseAdd = FALSE; _hdpa = DPA_Create(DPA_GetPtrCount(hdpa)); } SendMessage(_hwndTB, WM_SETREDRAW, FALSE, 0); if (_hdpa) { _NotifyBulkOperation(TRUE); // add buttons back in for (i = 0; i < DPA_GetPtrCount(hdpaToAdd); i++) { PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaToAdd, i); _OnFSNotifyAdd(poi->pidl, FSNA_BULKADD, poi->nOrder); } // remove buttons that die for (i = 0; i < DPA_GetPtrCount(hdpaToRemove); i++) { PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(hdpaToRemove, i); _OnFSNotifyRemove(poi->pidl); } _NotifyBulkOperation(FALSE); } if (fReleaseAdd) { DPA_Destroy(hdpaToAdd); } } DPA_Destroy(hdpaToRemove); } OrderList_Destroy(&hdpa); } SendMessage(_hwndTB, WM_SETREDRAW, TRUE, 0); if (fTakeOrderRef) { OrderList_Destroy(&hdpaSort); } _RememberOrder(); _UpdateButtons(); _SetDirty(FALSE); if (!SHIsTempDisplayMode()) { _ToolbarChanged(); } TraceMsg(TF_BAND, "SFToolbar::_FillToolbar found %d items", DPA_GetPtrCount(_hdpa)); } void CSFToolbar::EmptyToolbar() { if (_hwndTB) { TraceMsg(TF_BAND, "SFToolbar::EmptyToolbar %d items", _hdpa ? DPA_GetPtrCount(_hdpa) : 0); while (InlineDeleteButton(0)) { // delete the buttons } } OrderList_Destroy(&_hdpa); _fDirty = TRUE; _nNextCommandID = 0; } void CSFToolbar::_SetDirty(BOOL fDirty) { _fDirty = fDirty; } UINT CSFToolbar::_IndexToID(int iTBIndex) { TBBUTTON tbb; if (SendMessage(_hwndTB, TB_GETBUTTON, iTBIndex, (LPARAM)&tbb)) { return tbb.idCommand; } return (UINT)-1; } // if ptbbi is specified, dwMask must be filled in // // if pIndex is specified, it receives the DPA index, not the toolbar index HRESULT CSFToolbar::_GetButtonFromPidl(LPCITEMIDLIST pidl, TBBUTTONINFO * ptbbi, int * pIndex, LPITEMIDLIST *ppidlOut) { if (ppidlOut) *ppidlOut = NULL; if (!_hdpa) return E_FAIL; for (int i = DPA_GetPtrCount(_hdpa) - 1 ; i >= 0 ; i--) { HRESULT hr; PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, i); ASSERT(poi); if (pidl == poi->pidl) { hr = 0; } else { hr = (!pidl || !poi->pidl) ? E_FAIL : _psf->CompareIDs(0, pidl, poi->pidl); } if (ResultFromShort(0) == hr) { if (pIndex) *pIndex = i; if (ptbbi) { int id = _IndexToID(v_DPAIndexToTBIndex(i)); if (id != -1) { ptbbi->cbSize = sizeof(*ptbbi); if (-1 == ToolBar_GetButtonInfo(_hwndTB, id, ptbbi)) { ZeroMemory(ptbbi, sizeof(*ptbbi)); } } else { ZeroMemory(ptbbi, sizeof(*ptbbi)); } } if (ppidlOut) *ppidlOut = poi->pidl; return S_OK; } } return E_FAIL; } // On an add, tack the new button on the end void CSFToolbar::_OnFSNotifyAdd(LPCITEMIDLIST pidl, DWORD dwFlags, int nIndex) { // be paranoid and make sure we don't duplicate an item // if ((dwFlags & FSNA_BULKADD) || FAILED(_GetButtonFromPidl(pidl, NULL, NULL, NULL))) { LPITEMIDLIST pidlNew; if (_fFSNotify && !_ptscn && pidl && !(dwFlags & FSNA_BULKADD)) { if (FAILED(SHGetRealIDL(_psf, pidl, &pidlNew))) pidlNew = NULL; } else { pidlNew = pidl ? ILClone(pidl) : NULL; } if ((dwFlags & FSNA_BULKADD) || !_FilterPidl(pidlNew)) { int index = (dwFlags & FSNA_ADDDEFAULT) ? _DefaultInsertIndex() : nIndex; if (_fDropping) { if (-1 == _tbim.iButton) index = 0; // if qlinks has no items, _tbim.iButton is -1, but you can't insert there... else if (_tbim.dwFlags & TBIMHT_AFTER) index = _tbim.iButton + 1; else index = _tbim.iButton; // We need to store this as the new order because a drag and drop has occured. // We will store this order and use it until the end of time. _fHasOrder = TRUE; _fChangedOrder = TRUE; } _AddPidl(pidlNew, dwFlags, index); if (!(dwFlags & FSNA_BULKADD)) { OrderList_Reorder(_hdpa); } if (_fDropping) { _Dropped(index, FALSE); _fDropping = FALSE; } // NOTE: i'm nuking this call to SetDirty as it doesn't seem // necessary and we don't have a matching call to _SetDirty(FALSE); // mismatch of those calls causes nt5 bug #173868. [tjgreen 5-15-98] //_SetDirty(TRUE); } else { ILFree(pidlNew); } } } // This function syncronously removes the button, and deletes it's contents. // iTBIndex is a toolbar index, not a DPA index. // This avoids Reentrancy problems, as well as Leaks caused by unhooked toolbars BOOL_PTR CSFToolbar::InlineDeleteButton(int iTBIndex) { BOOL_PTR fRet = FALSE; TBBUTTONINFO tbbi = {0}; tbbi.cbSize = sizeof(tbbi); tbbi.dwMask = TBIF_LPARAM | TBIF_BYINDEX; if (ToolBar_GetButtonInfo(_hwndTB, iTBIndex, &tbbi) >= 0) { PIBDATA pibdata = (PIBDATA)tbbi.lParam; tbbi.lParam = NULL; ToolBar_SetButtonInfo(_hwndTB, iTBIndex, &tbbi); fRet = SendMessage(_hwndTB, TB_DELETEBUTTON, iTBIndex, 0); if (pibdata) delete pibdata; } return fRet; } // On a remove, rip out the old button and adjust existing ones void CSFToolbar::_OnFSNotifyRemove(LPCITEMIDLIST pidl) { int i; LPITEMIDLIST pidlButton; if (SUCCEEDED(_GetButtonFromPidl(pidl, NULL, &i, &pidlButton))) { // remove it from the DPA before nuking the button. There is a rentrancy issue here. DPA_DeletePtr(_hdpa, i); InlineDeleteButton(v_DPAIndexToTBIndex(i)); ILFree(pidlButton); _fChangedOrder = TRUE; } } // On a rename, just change the text of the old button // void CSFToolbar::_OnFSNotifyRename(LPCITEMIDLIST pidlFrom, LPCITEMIDLIST pidlTo) { TBBUTTONINFO tbbi; LPITEMIDLIST pidlButton; int i; tbbi.dwMask = TBIF_COMMAND | TBIF_LPARAM; if (SUCCEEDED(_GetButtonFromPidl(pidlFrom, &tbbi, &i, &pidlButton))) { LPITEMIDLIST pidlNew; if (_fFSNotify && !_ptscn) { if (FAILED(SHGetRealIDL(_psf, pidlTo, &pidlNew))) pidlNew = NULL; } else { pidlNew = ILClone(pidlTo); } if (pidlNew) { LPITEMIDLIST pidlFree = pidlNew; PORDERITEM poi = (PORDERITEM)DPA_FastGetPtr(_hdpa, i); if (EVAL(poi)) { pidlFree = poi->pidl; poi->pidl = pidlNew; TCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(_psf, pidlNew, SHGDN_NORMAL, szName, ARRAYSIZE(szName)))) { // _GetButtonFromPidl filled in tbbi.cbSize and tbbi.idCommand // PIBDATA pibdata = (PIBDATA)tbbi.lParam; if (pibdata) pibdata->SetOrderItem(poi); tbbi.dwMask = TBIF_TEXT; tbbi.pszText = szName; EVAL(ToolBar_SetButtonInfo(_hwndTB, tbbi.idCommand, &tbbi)); // Just so that it's new location gets persisted _fChangedOrder = TRUE; // sync up the orderlist right now so if an updatedir comes in // it won't think the renamed pidl is new _RememberOrder(); } } ILFree(pidlFree); } } } // On a complete update remove the old button and add it again // void CSFToolbar::_OnFSNotifyUpdate(LPCITEMIDLIST pidl) { TBBUTTONINFO tbbi; tbbi.dwMask = TBIF_COMMAND; LPITEMIDLIST pidlButton; if (SUCCEEDED(_GetButtonFromPidl(pidl, &tbbi, NULL, &pidlButton))) { TCHAR szName[MAX_PATH]; if (SUCCEEDED(DisplayNameOf(_psf, pidlButton, SHGDN_NORMAL, szName, ARRAYSIZE(szName)))) { int iBitmap = _GetBitmap(tbbi.idCommand, _IDToPibData(tbbi.idCommand, NULL), FALSE); if (iBitmap >= 0) { tbbi.dwMask = TBIF_IMAGE | TBIF_TEXT; tbbi.iImage = iBitmap; tbbi.pszText = szName; ToolBar_SetButtonInfo(_hwndTB, tbbi.idCommand, &tbbi); } } } } void CSFToolbar::_Refresh() { if (!_hdpa) return; _RememberOrder(); EmptyToolbar(); if (_fShow) { _FillToolbar(); } } LRESULT CSFToolbar::_OnTimer(WPARAM wParam) { return 0; } LRESULT CSFToolbar::_DefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DRAWITEM: case WM_MEASUREITEM: case WM_INITMENUPOPUP: case WM_MENUSELECT: if (_pcm2) _pcm2->HandleMenuMsg(uMsg, wParam, lParam); break; case WM_MENUCHAR: { LRESULT lres = 0; IContextMenu3* pcm3; if (_pcm2 && SUCCEEDED(_pcm2->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3)))) { pcm3->HandleMenuMsg2(uMsg, wParam, lParam, &lres); pcm3->Release(); } return lres; } break; case WM_TIMER: if (_OnTimer(wParam)) { return 1; } break; } return CNotifySubclassWndProc::_DefWindowProc(hwnd, uMsg, wParam, lParam); } /*---------------------------------------------------------- Purpose: For future use. when renaming a parent of this shell folder we should rebind to it and refill us. S_OK Indicates successful handling of this notification S_FALSE Indicates the notification is not a handled situation. The caller should handle the notification in this case. Other Failure code indicates a problem. Caller should abort operation or handle the notification itself. */ HRESULT CSFToolbar::_OnRenameFolder(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = S_FALSE; #if 0 // This code is just busted. It was for the case when we rename the parent. We don't support this if (!_IsChildID(pidl1, FALSE) || !_IsChildID(pidl2, FALSE)) { // Then this must be a parent of us. At this point we should rebind. The code that // was here did not work. I've removed it so that we can recode it in the future, but // now, we're just going to live with it TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameFolder: This is not a child folder."); hr = S_OK; } #endif return hr; } HRESULT CSFToolbar::OnChange(LONG lEvent, LPCITEMIDLIST pidlOrg1, LPCITEMIDLIST pidlOrg2) { HRESULT hr = S_OK; LPITEMIDLIST pidl1 = (LPITEMIDLIST)pidlOrg1; LPITEMIDLIST pidl2 = (LPITEMIDLIST)pidlOrg2; LPITEMIDLIST pidl1ToFree = NULL; // Used if we allocate a pidl that needs to be freed. (::TranslateIDs()) LPITEMIDLIST pidl2ToFree = NULL; LPITEMIDLIST pidlOut1Event2 = NULL; // Used if we allocate a pidl that needs to be freed. (::TranslateIDs()) LPITEMIDLIST pidlOut2Event2 = NULL; LONG lEvent2 = (LONG)-1; AddRef(); // This object could be released during this call if (_ptscn) { hr = _ptscn->TranslateIDs(&lEvent, pidlOrg1, pidlOrg2, &pidl1, &pidl2, &lEvent2, &pidlOut1Event2, &pidlOut2Event2); if (SUCCEEDED(hr)) { // if pidl1 doesn't equal pidlOrg1, then pidl1 was allocated and needs to be freed. pidl1ToFree = ((pidlOrg1 == pidl1) ? NULL : pidl1); pidl2ToFree = ((pidlOrg2 == pidl2) ? NULL : pidl2); ASSERT(NULL == pidl1 || IS_VALID_PIDL(pidl1)); ASSERT(NULL == pidl2 || IS_VALID_PIDL(pidl2)); } } if (SUCCEEDED(hr)) { hr = OnTranslatedChange(lEvent, pidl1, pidl2); // Do we have a second event to process? if (SUCCEEDED(hr) && lEvent2 != (LONG)-1) { // Yes, then go do it. hr = OnTranslatedChange(lEvent2, pidlOut1Event2, pidlOut2Event2); } ILFree(pidlOut1Event2); ILFree(pidlOut2Event2); ILFree(pidl1ToFree); ILFree(pidl2ToFree); } Release(); return hr; } #ifdef DEBUG void DBPrPidl(LPCSTR szPre, LPCITEMIDLIST pidl) { TCHAR szName[MAX_PATH]; szName[0] = '\0'; if (pidl) SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szName, SIZECHARS(szName), NULL); TraceMsg(TF_WARNING, "%hs%s", szPre, szName); return; } #endif HRESULT CSFToolbar::OnTranslatedChange(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = S_OK; BOOL fSizeChanged = FALSE; TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: lEvent=%x", lEvent); // If we weren't given a pidl we won't register for // SHChangeNotify calls, but our IShellChange interface // can still be QI()d so someone could errantly call us. // // If we change to using QS() for IShellChange interface // then we can put this check there... // if (NULL == _pidl) { // HACKHACK (scotth): resource-based menus (CMenuISF) don't set _pidl. // Right now allow SHCNE_UPDATEDIR thru... if (SHCNE_UPDATEDIR == lEvent) goto HandleUpdateDir; TraceMsg(TF_WARNING, "CSFToolbar::OnChange - _pidl is NULL"); hr = E_FAIL; goto CleanUp; } if (lEvent & SHCNE_PIDL1ISCHILD) { // We only handle notifications for immediate kids. (except SHCNE_RENAMEFOLDER) // if (!_IsChildID(pidl1, TRUE)) { TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Not a child. Bailing"); hr = E_FAIL; goto CleanUp; } } // This is no longer required. It also hinders us for several different notifications (Such as reorder notifies) #if 0 // If we're hidden we do not want to respond to any change notify // messages, so we unregister the toolbar. We mark the toolbar as // dirty so the next time we are shown, we will reenumerate the filesystem. if (!_fShow && !_fDropping) { _SetDirty(TRUE); _UnregisterChangeNotify(); hr = E_FAIL; goto CleanUp; } #endif // Have we been shown yet? if (_hdpa == NULL) { // No. Well, then punt this. We'll catch it on the first enum. hr = E_FAIL; goto CleanUp; } switch (lEvent) { case SHCNE_EXTENDED_EVENT: { SHChangeDWORDAsIDList UNALIGNED * pdwidl = (SHChangeDWORDAsIDList UNALIGNED *)pidl1; if (pdwidl->dwItem1 == SHCNEE_ORDERCHANGED) { TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Reorder event"); // Do this first so that we can say "We can handle it". This prevents the // mnfolder code that works around a bug in some installers where they don't // send a Create Folder before the create item in that folder. It causes an // update dir... if (!pidl2 || ILIsEqual(_pidl, pidl2)) { // if this reorder came from us, blow it off if (!SHChangeMenuWasSentByMe(this, pidl1)) { // load new order stream _LoadOrderStream(); // rebuild toolbar _SetDirty(TRUE); if (_fShow) _FillToolbar(); } hr = S_OK; } } } break; case SHCNE_DRIVEADD: case SHCNE_CREATE: case SHCNE_MKDIR: TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Adding item"); pidl1 = ILFindLastID(pidl1); _OnFSNotifyAdd(pidl1, FSNA_ADDDEFAULT, 0); _RememberOrder(); fSizeChanged = TRUE; break; case SHCNE_DRIVEREMOVED: case SHCNE_DELETE: case SHCNE_RMDIR: TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Removing item"); pidl1 = ILFindLastID(pidl1); _OnFSNotifyRemove(pidl1); _RememberOrder(); fSizeChanged = TRUE; break; case SHCNE_RENAMEFOLDER: TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameFolder"); // Break if notif is handled or if this is not for our kid. // hr = _OnRenameFolder(pidl1, pidl2); if (S_OK == hr) { fSizeChanged = TRUE; break; } TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameFolder Falling through to RenameItem"); // fall through case SHCNE_RENAMEITEM: { TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: RenameItem"); BOOL fOurKid1, fOurKid2; LPCITEMIDLIST p1 = pidl1; LPCITEMIDLIST p2 = pidl2; pidl1 = ILFindLastID(pidl1); pidl2 = ILFindLastID(pidl2); // An item can be renamed out of this folder. // Convert that into a remove. // fOurKid1 = _IsChildID(p1, TRUE); fOurKid2 = _IsChildID(p2, TRUE); if (fOurKid1 && fOurKid2) { TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Both are children"); _OnFSNotifyRename(pidl1, pidl2); fSizeChanged = TRUE; hr = S_OK; break; } else if (fOurKid1) { TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Only one is a child. Removing pidl 1"); _OnFSNotifyRemove(pidl1); fSizeChanged = TRUE; break; } else if (fOurKid2) { // An item can be renamed into this folder. // Convert that into an add. // TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Only one is a child. Adding pidl2"); _OnFSNotifyAdd(pidl2, FSNA_ADDDEFAULT, 0); fSizeChanged = TRUE; break; } else { // (we get here for guys below us who we don't care about, // and also for the fallthru from SHCNE_RENAMEFOLDER) TraceMsg(TF_SFTBAR, "CSFTBar::OnTranslateChange: Rename: Not our children"); /*NOTHING*/ hr = E_FAIL; } break; } case SHCNE_MEDIAINSERTED: case SHCNE_MEDIAREMOVED: case SHCNE_NETUNSHARE: if (_IsEqualID(pidl1)) goto HandleUpdateDir; case SHCNE_NETSHARE: case SHCNE_UPDATEITEM: if (_IsChildID(pidl1, TRUE)) { pidl1 = ILFindLastID(pidl1); _OnFSNotifyUpdate(pidl1); fSizeChanged = TRUE; } break; case SHCNE_UPDATEDIR: // in OnChange we picked off update dir notify and we didn't translate ids // now we can use ILIsEqual -- translate ids won't translate pidls in case // of update dir because it looks for immediate child of its, and fails when // it receives its own pidl // NOTE: When sftbar is registered recursivly, we only get the pidl of the // top pane. It is forwarded down to the children. Since this is now a "Child" // of the top pane, we check to see if this pidl is a child of that pidl, hence the // ILIsParent(pidl1, _pidl) // HACKHACK, HUGE HACK: normaly w/ update dir pidl2 is NULL but in start menu // augmergeisf can change some other notify (e.g. rename folder) to update dir // in which case pidl2 is not null and we have to see if it is our child to do the // update (11/18/98) reljai if (_IsEqualID(pidl1) || // Calling UpdateDir on _THIS_ folder _IsChildID(pidl1, FALSE) || // FEATURE (lamadio) Is this needed? _IsChildID(pidl2, FALSE) || // A changed to update (see comment) _IsParentID(pidl1)) // Some parent in the chain (because it's recursive) { HandleUpdateDir: // NOTE: if a series of UPDATEIMAGE notifies gets // translated to UPDATEDIR and we flicker-perf // _FillToolbar, we may lose image updates // (in which case, _Refresh would fix it) // _SetDirty(TRUE); _FillToolbar(); } break; case SHCNE_ASSOCCHANGED: IEInvalidateImageList(); // We may need to use different icons. _Refresh(); // full refresh for now. break; case SHCNE_UPDATEIMAGE: // global if (pidl1) { int iImage = *(int UNALIGNED *)((BYTE *)pidl1 + 2); IEInvalidateImageList(); // We may need to use different icons. if (pidl2) { iImage = SHHandleUpdateImage(pidl2); if (iImage == -1) { break; } } if (iImage == -1 || TBHasImage(_hwndTB, iImage)) { _UpdateIconSize(_uIconSize, TRUE); _Refresh(); } fSizeChanged = TRUE; } else { _Refresh(); } break; default: hr = E_FAIL; break; } if (fSizeChanged) { if (_hwndPager) SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0); _ToolbarChanged(); } CleanUp: return hr; } BOOL TBHasImage(HWND hwnd, int iImageIndex) { BOOL fRefresh = FALSE; for (int i = ToolBar_ButtonCount(hwnd) - 1 ; i >= 0 ; i--) { TBBUTTON tbb; if (SendMessage(hwnd, TB_GETBUTTON, i, (LPARAM)&tbb)) { if (tbb.iBitmap == iImageIndex) { fRefresh = TRUE; break; } } } return fRefresh; } void CSFToolbar::_SetToolbarState() { SHSetWindowBits(_hwndTB, GWL_STYLE, TBSTYLE_LIST, (_uIconSize != ISFBVIEWMODE_SMALLICONS || _fNoShowText) ? 0 : TBSTYLE_LIST); } int CSFToolbar::_DefaultInsertIndex() { return DA_LAST; } BOOL CSFToolbar::_IsParentID(LPCITEMIDLIST pidl) { // Is the pidl passed in a parent of one of the IDs in the namespace // or the only one i've got? if (_ptscn) return S_OK == _ptscn->IsEqualID(NULL, pidl); else return ILIsParent(pidl, _pidl, FALSE); } BOOL CSFToolbar::_IsEqualID(LPCITEMIDLIST pidl) { if (_ptscn) return S_OK == _ptscn->IsEqualID(pidl, NULL); else return ILIsEqual(_pidl, pidl); } BOOL CSFToolbar::_IsChildID(LPCITEMIDLIST pidlChild, BOOL fImmediate) { BOOL fRet = FALSE; if (pidlChild) { if (_ptscn) fRet = S_OK == _ptscn->IsChildID(pidlChild, fImmediate); else fRet = ILIsParent(_pidl, pidlChild, fImmediate); } return fRet; } void CSFToolbar::v_CalcWidth(int* pcxMin, int* pcxMax) { ASSERT(IS_VALID_WRITE_PTR(pcxMin, int)); ASSERT(IS_VALID_WRITE_PTR(pcxMax, int)); // Calculate a decent button width given current state HIMAGELIST himl; int cxMax = 0; int cxMin = 0; himl = (HIMAGELIST)SendMessage(_hwndTB, TB_GETIMAGELIST, 0, 0); if (himl) { int cy; // Start with the width of the button ImageList_GetIconSize(himl, &cxMax, &cy); // We want at least a bit of space around the icon if (_uIconSize != ISFBVIEWMODE_SMALLICONS) cxMax += 20; else cxMax += 4 * GetSystemMetrics(SM_CXEDGE); } // Add in any additional space needed // Text takes up a bit more space if (!_fNoShowText) { cxMax += 20; // Horizontal text takes up a lot // if we're smallicon with text (horizontal button) // mode, use the minimized metric to mimic the taskbar if (_uIconSize == ISFBVIEWMODE_SMALLICONS) cxMax = GetSystemMetrics(SM_CXMINIMIZED); } *pcxMin = cxMin; *pcxMax = cxMax; } // Adjust buttons based on current state. // void CSFToolbar::_UpdateButtons() { if (_hwndTB) { // set "list" (text on right) or not (text underneath) // NOTE: list mode always displays some text, don't do it if no text _SetToolbarState(); v_CalcWidth(&_cxMin, &_cxMax); SendMessage(_hwndTB, TB_SETBUTTONWIDTH, 0, MAKELONG(_cxMin, _cxMax)); // We just changed the layout // SendMessage(_hwndTB, TB_AUTOSIZE, 0, 0); if (_hwndPager) { LRESULT lButtonSize = SendMessage(_hwndTB, TB_GETBUTTONSIZE, 0, 0); Pager_SetScrollInfo(_hwndPager, 50, 1, HIWORD(lButtonSize)); SendMessage(_hwndPager, PGMP_RECALCSIZE, (WPARAM) 0, (LPARAM) 0); } } } /*---------------------------------------------------------- Purpose: Helper function that calls IShellFolder::GetUIObjectOf(). Returns: pointer to the requested interface NULL if failed */ void *CSFToolbar::_GetUIObjectOfPidl(LPCITEMIDLIST pidl, REFIID riid) { LPCITEMIDLIST * apidl = &pidl; void *pv; if (FAILED(_psf->GetUIObjectOf(GetHWNDForUIObject(), 1, apidl, riid, 0, &pv))) { pv = NULL; } return pv; } INT_PTR CALLBACK CSFToolbar::_RenameDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { ASSERT(lParam); SetWindowLongPtr(hDlg, DWLP_USER, lParam); EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); // cross-lang platform support SHSetDefaultDialogFont(hDlg, IDD_NAME); HWND hwndEdit = GetDlgItem(hDlg, IDD_NAME); SendMessage(hwndEdit, EM_LIMITTEXT, MAX_PATH - 1, 0); TCHAR szText[MAX_PATH + 80]; TCHAR szTemplate[80]; HWND hwndLabel = GetDlgItem(hDlg, IDD_PROMPT); GetWindowText(hwndLabel, szTemplate, ARRAYSIZE(szTemplate)); wnsprintf(szText, ARRAYSIZE(szText), szTemplate, lParam); SetWindowText(hwndLabel, szText); SetWindowText(hwndEdit, (LPTSTR)lParam); break; } case WM_DESTROY: SHRemoveDefaultDialogFont(hDlg); return FALSE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDD_NAME: { if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_UPDATE) { LPTSTR lpstrName = (LPTSTR) GetWindowLongPtr(hDlg, DWLP_USER); EnableOKButtonFromID(hDlg, IDD_NAME); GetDlgItemText(hDlg, IDD_NAME, lpstrName, MAX_PATH); } break; } case IDOK: { TCHAR pszTmp[MAX_PATH]; StrCpy(pszTmp, (LPTSTR) GetWindowLongPtr(hDlg, DWLP_USER)); if (PathCleanupSpec(NULL,pszTmp)) { HWND hwnd; ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_FAVS_INVALIDFN), MAKEINTRESOURCE(IDS_FAVS_ADDTOFAVORITES), MB_OK | MB_ICONHAND); hwnd = GetDlgItem(hDlg, IDD_NAME); SetWindowText(hwnd, TEXT("\0")); EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); SetFocus(hwnd); break; } } // fall through case IDCANCEL: EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam)); break; default: return FALSE; } break; default: return FALSE; } return TRUE; } // This window proc is used for a temporary worker window that is used to position dialogs // as well as maintain the correct Z-Order // NOTE: This is used in mnfolder as well. LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { // Make sure activation tracks back to the parent. case WM_ACTIVATE: { if (WA_ACTIVE != LOWORD(wParam)) goto DefWnd; SetActiveWindow(GetParent(hwnd)); return FALSE; } case WM_WINDOWPOSCHANGING: { WINDOWPOS* pwp = (WINDOWPOS*)lParam; pwp->flags |= SWP_NOOWNERZORDER; } break; } DefWnd: return DefWindowProc(hwnd, uMsg, wParam, lParam); } HWND CSFToolbar::CreateWorkerWindow() { if (!_hwndWorkerWindow) { _hwndWorkerWindow = SHCreateWorkerWindow(HiddenWndProc, GetHWNDForUIObject(), WS_EX_TOOLWINDOW /*| WS_EX_TOPMOST */, WS_POPUP, 0, _hwndTB); } return _hwndWorkerWindow; } HRESULT CSFToolbar::_OnRename(POINT *ppt, int id) { ASSERT(_psf); TCHAR szName[MAX_PATH]; LPITEMIDLIST pidl = ILClone(_IDToPidl(id)); if (!pidl) return E_OUTOFMEMORY; _ObtainPIDLName(pidl, szName, ARRAYSIZE(szName)); // create a temp window so that placement of the dialog will be close to the point. // do this so that we'll use USER's code to get placement correctly w/ respect to multimon and work area _hwndWorkerWindow = CreateWorkerWindow(); SetWindowPos(_hwndWorkerWindow, NULL, ppt->x, ppt->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); // Now the horrible work of disabling our UI parent window so we can go modal. // In an ideal world, we would pass our true parent window and USER will do // all the work of modality, but we have to use our worker window thingie // to get the dialog positioned correctly with respect to multimon, // so we have to find the modal parent and disable him the hard way. // IUnknown *punkSite; IUnknown *punkTLB; // Doesn't matter what we SAFECAST "this" to; just pick something to keep the compiler happy IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite)); IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IUnknown, &punkTLB)); // Tell OLE to go modal HRESULT hrModeless = IUnknown_EnableModless(punkTLB, FALSE); // Tell USER to go modal HWND hwndDisable; IUnknown_GetWindow(punkTLB, &hwndDisable); BOOL bPrevEnabled = FALSE; while (hwndDisable && (GetWindowLong(hwndDisable, GWL_STYLE) & WS_CHILD)) hwndDisable = GetParent(hwndDisable); if (hwndDisable) bPrevEnabled = !EnableWindow(hwndDisable, FALSE); // return value of EnableWindow needs to be negated. while (1) { if (DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_ISFBANDRENAME), _hwndWorkerWindow, _RenameDlgProc, (LPARAM)szName) != IDOK) break; WCHAR wsz[MAX_PATH]; SHTCharToUnicode(szName, wsz, ARRAYSIZE(wsz)); // Must re-assert TOPMOSTness so SetNameOf UI will be visible. // (We lose it when the user dismisses the dialog box above.) // Curiously, the worker window is owned by the app's window, not the // menu, so the worker window ends up fighting with the menu to see who is on top SetWindowPos(_hwndWorkerWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); if (SUCCEEDED(_psf->SetNameOf(_hwndWorkerWindow, pidl, wsz, 0, NULL))) { SHChangeNotifyHandleEvents(); _SaveOrderStream(); break; } } // (must undo modality in reverse order) // Tell USER to return to modeless (as appropriate) if (hwndDisable) EnableWindow(hwndDisable, bPrevEnabled); // Tell OLE to return to modeless (as appropriate) if (SUCCEEDED(hrModeless)) IUnknown_EnableModless(punkTLB, TRUE); ATOMICRELEASE(punkTLB); ATOMICRELEASE(punkSite); ILFree(pidl); return S_OK; } BOOL CSFToolbar::_UpdateIconSize(UINT uIconSize, BOOL fUpdateButtons) { BOOL fChanged = (_uIconSize != uIconSize); _uIconSize = uIconSize; TraceMsg(TF_BAND, "ISFBand::_UpdateIconSize going %hs", (_uIconSize == ISFBVIEWMODE_LARGEICONS ? "LARGE" : (_uIconSize == ISFBVIEWMODE_SMALLICONS ? "SMALL" : "LOGOS"))); if (_hwndTB) { ATOMICRELEASE(_piml); if (!_fNoIcons) { int iImageList = (_uIconSize == ISFBVIEWMODE_LARGEICONS) ? SHIL_LARGE : SHIL_SYSSMALL; SHGetImageList(iImageList, IID_PPV_ARG(IImageList, &_piml)); } // sending a null himl is significant.. it means no image list SendMessage(_hwndTB, TB_SETIMAGELIST, 0, (LPARAM)_piml); if (fUpdateButtons) _UpdateButtons(); } return fChanged; } HMENU CSFToolbar::_GetContextMenu(IContextMenu* pcm, int* pid) { HMENU hmenu = CreatePopupMenu(); if (hmenu) { UINT fFlags = CMF_CANRENAME; if (0 > GetKeyState(VK_SHIFT)) fFlags |= CMF_EXTENDEDVERBS; pcm->QueryContextMenu(hmenu, 0, *pid, CONTEXTMENU_IDCMD_LAST, fFlags); } return hmenu; } void CSFToolbar::_OnDefaultContextCommand(int idCmd) { } HRESULT CSFToolbar::_GetTopBrowserWindow(HWND* phwnd) { IUnknown * punkSite; HRESULT hr = IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite)); if (SUCCEEDED(hr)) { hr = SHGetTopBrowserWindow(punkSite, phwnd); punkSite->Release(); } return hr; } HRESULT CSFToolbar::_OnOpen(int id, BOOL fExplore) { HRESULT hr = E_FAIL; LPCITEMIDLIST pidl = _IDToPidl(id); if (pidl) { IUnknown* punkSite; hr = IUnknown_GetSite(SAFECAST(this, IWinEventHandler*), IID_PPV_ARG(IUnknown, &punkSite)); if (SUCCEEDED(hr)) { DWORD dwFlags = SBSP_DEFBROWSER | SBSP_DEFMODE; if (fExplore) dwFlags |= SBSP_EXPLOREMODE; hr = SHNavigateToFavorite(_psf, pidl, punkSite, dwFlags); punkSite->Release(); } } return hr; } HRESULT CSFToolbar::_HandleSpecialCommand(IContextMenu* pcm, PPOINT ppt, int id, int idCmd) { TCHAR szCommandString[40]; HRESULT hr = ContextMenu_GetCommandStringVerb(pcm, idCmd, szCommandString, ARRAYSIZE(szCommandString)); if (SUCCEEDED(hr)) { if (lstrcmpi(szCommandString, TEXT("rename")) == 0) return _OnRename(ppt, id); else if (lstrcmpi(szCommandString, TEXT("open")) == 0) return _OnOpen(id, FALSE); else if (lstrcmpi(szCommandString, TEXT("explore")) == 0) return _OnOpen(id, TRUE); } return S_FALSE; } LRESULT CSFToolbar::_DoContextMenu(IContextMenu* pcm, LPPOINT ppt, int id, LPRECT prcExclude) { LRESULT lres = 0; int idCmdFirst = CONTEXTMENU_IDCMD_FIRST; HMENU hmContext = _GetContextMenu(pcm, &idCmdFirst); if (hmContext) { int idCmd; if (_hwndToolTips) SendMessage(_hwndToolTips, TTM_ACTIVATE, FALSE, 0L); TPMPARAMS tpm; TPMPARAMS * ptpm = NULL; if (prcExclude) { tpm.cbSize = sizeof(tpm); tpm.rcExclude = *((LPRECT)prcExclude); ptpm = &tpm; } idCmd = TrackPopupMenuEx(hmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, ppt->x, ppt->y, _hwndTB, ptpm); if (_hwndToolTips) SendMessage(_hwndToolTips, TTM_ACTIVATE, TRUE, 0L); if (idCmd) { if (idCmd < idCmdFirst) { _OnDefaultContextCommand(idCmd); } else { idCmd -= idCmdFirst; if (_HandleSpecialCommand(pcm, ppt, id, idCmd) != S_OK) { _hwndWorkerWindow = CreateWorkerWindow(); SetWindowPos(_hwndWorkerWindow, NULL, ppt->x, ppt->y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); CMINVOKECOMMANDINFO ici = { sizeof(CMINVOKECOMMANDINFO), 0, _hwndWorkerWindow, MAKEINTRESOURCEA(idCmd), NULL, NULL, SW_NORMAL, }; pcm->InvokeCommand(&ici); } } } // if we get this far // we need to return handled so that WM_CONTEXTMENU doesn't come through lres = 1; DestroyMenu(hmContext); } return lres; } LRESULT CSFToolbar::_OnContextMenu(WPARAM wParam, LPARAM lParam) { LRESULT lres = 0; RECT rc; LPRECT prcExclude = NULL; POINT pt; int i; if (lParam != (LPARAM)-1) { pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); POINT pt2 = pt; MapWindowPoints(HWND_DESKTOP, _hwndTB, &pt2, 1); i = ToolBar_HitTest(_hwndTB, &pt2); } else { // keyboard context menu. i = (int)SendMessage(_hwndTB, TB_GETHOTITEM, 0, 0); if (i >= 0) { SendMessage(_hwndTB, TB_GETITEMRECT, i, (LPARAM)&rc); MapWindowPoints(_hwndTB, HWND_DESKTOP, (LPPOINT)&rc, 2); pt.x = rc.left; pt.y = rc.bottom; prcExclude = &rc; } } TraceMsg(TF_BAND, "NM_RCLICK %d,%d = %d", pt.x, pt.y, i); if (i >= 0) { UINT id = _IndexToID(i); if (-1 != id) { LPCITEMIDLIST pidl = _IDToPidl(id, NULL); if (pidl) { LPCONTEXTMENU pcm = (LPCONTEXTMENU)_GetUIObjectOfPidl(pidl, IID_IContextMenu); if (pcm) { // grab pcm2 for owner draw support pcm->QueryInterface(IID_PPV_ARG(IContextMenu2, &_pcm2)); ToolBar_MarkButton(_hwndTB, id, TRUE); lres = _DoContextMenu(pcm, &pt, id, prcExclude); ToolBar_MarkButton(_hwndTB, id, FALSE); if (lres) _FlushNotifyMessages(_hwndTB); ATOMICRELEASE(_pcm2); pcm->Release(); } } } } return lres; } LRESULT CSFToolbar::_OnCustomDraw(NMCUSTOMDRAW* pnmcd) { return CDRF_DODEFAULT; } void CSFToolbar::_OnDragBegin(int iItem, DWORD dwPreferredEffect) { LPCITEMIDLIST pidl = _IDToPidl(iItem, &_iDragSource); ToolBar_SetHotItem(_hwndTB, _iDragSource); if (_hwndTB && pidl) DragDrop(_hwndTB, _psf, pidl, dwPreferredEffect, NULL); _iDragSource = -1; } LRESULT CSFToolbar::_OnHotItemChange(NMTBHOTITEM * pnm) { LPNMTBHOTITEM lpnmhi = (LPNMTBHOTITEM)pnm; if (_hwndPager && (lpnmhi->dwFlags & (HICF_ARROWKEYS | HICF_ACCELERATOR))) { int iOldPos, iNewPos; RECT rc, rcPager; int heightPager; int iSelected = lpnmhi->idNew; iOldPos = (int)SendMessage(_hwndPager, PGM_GETPOS, (WPARAM)0, (LPARAM)0); iNewPos = iOldPos; SendMessage(_hwndTB, TB_GETITEMRECT, (WPARAM)iSelected, (LPARAM)&rc); if (rc.top < iOldPos) { iNewPos =rc.top; } GetClientRect(_hwndPager, &rcPager); heightPager = RECTHEIGHT(rcPager); if (rc.top >= iOldPos + heightPager) { iNewPos += (rc.bottom - (iOldPos + heightPager)) ; } if (iNewPos != iOldPos) SendMessage(_hwndPager, PGM_SETPOS, (WPARAM)0, (LPARAM)iNewPos); } return 0; } void CSFToolbar::_OnToolTipsCreated(NMTOOLTIPSCREATED* pnm) { _hwndToolTips = pnm->hwndToolTips; SHSetWindowBits(_hwndToolTips, GWL_STYLE, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX, TTS_ALWAYSTIP | TTS_TOPMOST | TTS_NOPREFIX); // set the AutoPopTime (the duration of showing the tooltip) to a large value SendMessage(_hwndToolTips, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)MAXSHORT); } LRESULT CSFToolbar::_OnNotify(LPNMHDR pnm) { LRESULT lres = 0; //The following statement traps all pager control notification messages. if ((pnm->code <= PGN_FIRST) && (pnm->code >= PGN_LAST)) { return SendMessage(_hwndTB, WM_NOTIFY, (WPARAM)0, (LPARAM)pnm); } switch (pnm->code) { case TBN_DRAGOUT: { TBNOTIFY *ptbn = (TBNOTIFY*)pnm; _OnDragBegin(ptbn->iItem, 0); lres = 1; break; } case TBN_HOTITEMCHANGE: _OnHotItemChange((LPNMTBHOTITEM)pnm); break; case TBN_GETINFOTIP: { LPNMTBGETINFOTIP pnmTT = (LPNMTBGETINFOTIP)pnm; UINT uiCmd = pnmTT->iItem; DWORD dwFlags = _fNoShowText ? QITIPF_USENAME | QITIPF_LINKNOTARGET : QITIPF_LINKNOTARGET; if (!GetInfoTipEx(_psf, dwFlags, _IDToPidl(uiCmd), pnmTT->pszText, pnmTT->cchTextMax)) { TBBUTTONINFO tbbi; tbbi.cbSize = sizeof(tbbi); tbbi.dwMask = TBIF_TEXT; tbbi.pszText = pnmTT->pszText; tbbi.cchText = pnmTT->cchTextMax; lres = (-1 != ToolBar_GetButtonInfo(_hwndTB, uiCmd, &tbbi)); } break; } //WARNING: Right now I am calling the same function for both A and W version if this notification supports // Strings then it needs to thunk. Right now its only used for image case TBN_GETDISPINFOA: _OnGetDispInfo(pnm, FALSE); break; case TBN_GETDISPINFOW: _OnGetDispInfo(pnm, TRUE); break; case NM_TOOLTIPSCREATED: _OnToolTipsCreated((NMTOOLTIPSCREATED*)pnm); break; case NM_RCLICK: lres = _OnContextMenu(NULL, GetMessagePos()); break; case NM_CUSTOMDRAW: return _OnCustomDraw((NMCUSTOMDRAW*)pnm); } return lres; } DWORD CSFToolbar::_GetAttributesOfPidl(LPCITEMIDLIST pidl, DWORD dwAttribs) { if (FAILED(_psf->GetAttributesOf(1, &pidl, &dwAttribs))) dwAttribs = 0; return dwAttribs; } PIBDATA CSFToolbar::_PosToPibData(UINT iPos) { ASSERT(IsWindow(_hwndTB)); // Initialize to NULL in case the GetButton Fails. TBBUTTON tbb = {0}; PIBDATA pibData = NULL; if (ToolBar_GetButton(_hwndTB, iPos, &tbb)) { pibData = (PIBDATA)tbb.dwData; } return pibData; } PIBDATA CSFToolbar::_IDToPibData(UINT uiCmd, int * piPos) { PIBDATA pibdata = NULL; // Initialize to NULL in case the GetButtonInfo Fails. TBBUTTONINFO tbbi = {0}; tbbi.cbSize = sizeof(tbbi); tbbi.dwMask = TBIF_LPARAM; int iPos = ToolBar_GetButtonInfo(_hwndTB, uiCmd, &tbbi); if (iPos >= 0) pibdata = (PIBDATA)tbbi.lParam; if (piPos) *piPos = iPos; return pibdata; } LPCITEMIDLIST CSFToolbar::_IDToPidl(UINT uiCmd, int *piPos) { LPCITEMIDLIST pidl; PIBDATA pibdata = _IDToPibData(uiCmd, piPos); if (pibdata) pidl = pibdata->GetPidl(); else pidl = NULL; return pidl; } /*---------------------------------------------------------- Purpose: IWinEventHandler::OnWinEvent method Processes messages passed on from the bandsite. */ HRESULT CSFToolbar::OnWinEvent(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { *plres = 0; // We are addref'n here because during the course of the // Context menu, the view could be changed which free's the menu. // We will release after we're sure the this pointer is no longer needed. AddRef(); switch (uMsg) { case WM_SYSCOLORCHANGE: SendMessage(_hwndTB, uMsg, wParam, lParam); InvalidateRect(_hwndTB, NULL, TRUE); break; case WM_PALETTECHANGED: InvalidateRect(_hwndTB, NULL, FALSE); SendMessage(_hwndTB, uMsg, wParam, lParam); break; case WM_COMMAND: *plres = _OnCommand(wParam, lParam); break; case WM_NOTIFY: *plres = _OnNotify((LPNMHDR)lParam); break; case WM_CONTEXTMENU: *plres = _OnContextMenu(wParam, lParam); break; } Release(); return S_OK; } // Map the information loaded (or ctor) into _psf, [_pidl] // HRESULT CSFToolbar::_AfterLoad() { HRESULT hr = S_OK; // if we have a pidl then we need to get ready // for notifications... // if (_pidl) { // pidls must be rooted off the desktop // _fFSNotify = TRUE; // shortcut -- just specifying a pidl is good enough // if (!_psf) { _fPSFBandDesktop = TRUE; hr = IEBindToObject(_pidl, &_psf); } } return hr; } // IDropTarget implementation HRESULT CSFToolbar::GetWindowsDDT(HWND * phwndLock, HWND * phwndScroll) { *phwndLock = _hwndTB; *phwndScroll = _hwndTB; return S_OK; } HRESULT CSFToolbar::HitTestDDT(UINT nEvent, LPPOINT ppt, DWORD_PTR * pdwId, DWORD *pdwEffect) { TBINSERTMARK tbim; switch (nEvent) { case HTDDT_ENTER: return S_OK; case HTDDT_OVER: { int iButton = IBHT_BACKGROUND; // assume we hit the background // if we're the source, this may be a move operation // *pdwEffect = (_iDragSource >= 0) ? DROPEFFECT_MOVE : DROPEFFECT_NONE; if (!ToolBar_InsertMarkHitTest(_hwndTB, ppt, &tbim)) { if (tbim.dwFlags & TBIMHT_BACKGROUND) { RECT rc; GetClientRect(_hwndTB, &rc); // are we outside the toolbar window entirely? if (!PtInRect(&rc, *ppt)) { // rebar already did the hittesting so we are on the rebar // but not the toolbar => we are in the title part if (!_AllowDropOnTitle()) { // yes; don't allow drop here iButton = IBHT_OUTSIDEWINDOW; *pdwEffect = DROPEFFECT_NONE; } // set tbim.iButton to invalid value so we don't draw insert mark tbim.iButton = -1; } } else { // nope, we hit a real button // if (tbim.iButton == _iDragSource) { iButton = IBHT_SOURCE; // don't drop on the source button } else { iButton = tbim.iButton; } tbim.iButton = IBHT_BACKGROUND; // we never force a move operation if we're on a real button *pdwEffect = DROPEFFECT_NONE; } } *pdwId = iButton; } break; case HTDDT_LEAVE: // Reset tbim.iButton = IBHT_BACKGROUND; tbim.dwFlags = 0; break; default: return E_INVALIDARG; } // update ui if (tbim.iButton != _tbim.iButton || tbim.dwFlags != _tbim.dwFlags) { if (ppt) _tbim = tbim; // for now I don't want to rely on non-filesystem IShellFolder // implementations to call our OnChange method when a drop occurs, // so don't even show the insert mark. // if (_fFSNotify || _iDragSource >= 0) { DAD_ShowDragImage(FALSE); ToolBar_SetInsertMark(_hwndTB, &tbim); DAD_ShowDragImage(TRUE); } } return S_OK; } HRESULT CSFToolbar::GetObjectDDT(DWORD_PTR dwId, REFIID riid, void ** ppvObj) { HRESULT hr = E_NOINTERFACE; *ppvObj = NULL; if ((IBHT_SOURCE == dwId) || (IBHT_OUTSIDEWINDOW == dwId)) { // do nothing } else if (IBHT_BACKGROUND == dwId) { // nash:41937: not sure how, but _psf can be NULL... if (EVAL(_psf)) hr = _psf->CreateViewObject(_hwndTB, riid, ppvObj); } else { LPCITEMIDLIST pidl = _IDToPidl((UINT)dwId, NULL); if (pidl) { *ppvObj = _GetUIObjectOfPidl(pidl, riid); if (*ppvObj) hr = S_OK; } } //TraceMsg(TF_BAND, "SFToolbar::GetObject(%d) returns %x", dwId, hr); return hr; } HRESULT CSFToolbar::_SaveOrderStream() { if (_fChangedOrder) { // Notify everyone that the order changed SHSendChangeMenuNotify(this, SHCNEE_ORDERCHANGED, 0, _pidl); _fChangedOrder = FALSE; return S_OK; } else return S_FALSE; } void CSFToolbar::_Dropped(int nIndex, BOOL fDroppedOnSource) { _fDropped = TRUE; _fChangedOrder = TRUE; // Save new order stream _SaveOrderStream(); if (fDroppedOnSource) _FlushNotifyMessages(_hwndTB); } /*---------------------------------------------------------- Purpose: CDelegateDropTarget::OnDropDDT */ HRESULT CSFToolbar::OnDropDDT(IDropTarget *pdt, IDataObject *pdtobj, DWORD * pgrfKeyState, POINTL pt, DWORD *pdwEffect) { // Are we NOT the drag source? if (_iDragSource == -1) { // No, we're not. Well, then the source may be the chevron menu // representing the hidden items in this menu. Let's check LPITEMIDLIST pidl; if (SUCCEEDED(SHPidlFromDataObject2(pdtobj, &pidl))) { // We've got a pidl, Are we the parent? Do we have a button? int iIndex; if (ILIsParent(_pidl, pidl, TRUE) && SUCCEEDED(_GetButtonFromPidl(ILFindLastID(pidl), NULL, &iIndex, NULL))) { // We are the parent! Then let's copy that down and set it // as the drag source so that down below we reorder. _iDragSource = iIndex; } ILFree(pidl); } } if (_iDragSource >= 0) { if (_fAllowReorder) { TraceMsg(TF_BAND, "SFToolbar::OnDrop reorder %d to %d %s", _iDragSource, _tbim.iButton, _tbim.dwFlags & TBIMHT_AFTER ? "A" : "B"); int iNewLocation = _tbim.iButton; if (_tbim.dwFlags & TBIMHT_AFTER) iNewLocation++; if (iNewLocation > _iDragSource) iNewLocation--; if (ToolBar_MoveButton(_hwndTB, _iDragSource, iNewLocation)) { PORDERITEM poi = (PORDERITEM)DPA_DeletePtr(_hdpa, v_TBIndexToDPAIndex(_iDragSource)); if (poi) { DPA_InsertPtr(_hdpa, v_TBIndexToDPAIndex(iNewLocation), poi); OrderList_Reorder(_hdpa); // If we're dropping again, then we don't need the _hdpaOrder... OrderList_Destroy(&_hdpaOrder); // A reorder has occurred. We need to use the order stream as the order... _fHasOrder = TRUE; _fDropping = TRUE; _Dropped(iNewLocation, TRUE); _fDropping = FALSE; _RememberOrder(); _SetDirty(TRUE); } } } // Don't forget to reset this! _iDragSource = -1; DragLeave(); } else { // We want to override the default to be LINK (SHIFT+CONTROL) if (0 == DataObj_GetDWORD(pdtobj, g_cfPreferredDropEffect, 0)) { if (!(*pgrfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT))) { // NOTE: not all data objects will allow us to call SetData() DataObj_SetDWORD(pdtobj, g_cfPreferredDropEffect, DROPEFFECT_LINK); } } _fDropping = TRUE; return S_OK; } return S_FALSE; } void CSFToolbar::_SortDPA(HDPA hdpa) { // If we don't have a _psf, then we certainly can't sort it // If we don't have a hdpa, then we certainly can't sort it // If the hdpa is empty, then there's no point in sorting it if (_psf && hdpa && DPA_GetPtrCount(hdpa)) { ORDERINFO oinfo; oinfo.psf = _psf; oinfo.psf->AddRef(); oinfo.dwSortBy = (_fNoNameSort ? OI_SORTBYORDINAL : OI_SORTBYNAME); DPA_Sort(hdpa, OrderItem_Compare, (LPARAM)&oinfo); oinfo.psf->Release(); } } void CSFToolbar::_RememberOrder() { OrderList_Destroy(&_hdpaOrder); if (_hdpa) { _hdpaOrder = OrderList_Clone(_hdpa); _SortDPA(_hdpaOrder); } } HMENU CSFToolbar::_GetBaseContextMenu() { HMENU hmenu = SHLoadMenuPopup(HINST_THISDLL, MENU_ISFBAND); // no logo view, remove the menu item... HMENU hView = GetSubMenu(hmenu, 0); DeleteMenu(hView, ISFBIDM_LOGOS, MF_BYCOMMAND); return hmenu; } HMENU CSFToolbar::_GetContextMenu() { HMENU hmenuSrc = _GetBaseContextMenu(); if (hmenuSrc) { MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE; mii.fState = MF_CHECKED; UINT uCmdId = ISFBIDM_LOGOS; if (_uIconSize != ISFBVIEWMODE_LOGOS) uCmdId = (_uIconSize == ISFBVIEWMODE_LARGEICONS ? ISFBIDM_LARGE : ISFBIDM_SMALL); SetMenuItemInfo(hmenuSrc, uCmdId, MF_BYCOMMAND, &mii); if (!_fNoShowText) SetMenuItemInfo(hmenuSrc, ISFBIDM_SHOWTEXT, MF_BYCOMMAND, &mii); if (!_fFSNotify || !_pidl || ILIsEmpty(_pidl)) DeleteMenu(hmenuSrc, ISFBIDM_OPEN, MF_BYCOMMAND); HMENU hView = GetSubMenu(hmenuSrc, 0); DeleteMenu(hView, ISFBIDM_LOGOS, MF_BYCOMMAND); } return hmenuSrc; } // IContextMenu implementation // HRESULT CSFToolbar::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HMENU hmenuSrc = _GetContextMenu(); int i = 0; if (hmenuSrc) { i += Shell_MergeMenus(hmenu, hmenuSrc, indexMenu, idCmdFirst, idCmdLast, 0); DestroyMenu(hmenuSrc); } if (!_pcmSF && _fAllowRename && _psf) { _psf->CreateViewObject(_hwndTB, IID_PPV_ARG(IContextMenu, &_pcmSF)); } if (_pcmSF) { _idCmdSF = i - idCmdFirst; HRESULT hrT = _pcmSF->QueryContextMenu(hmenu, indexMenu + i, i, 0x7fff, CMF_BANDCMD); if (SUCCEEDED(hrT)) i += HRESULT_CODE(hrT); } return i; } BOOL CSFToolbar::_UpdateShowText(BOOL fNoShowText) { BOOL fChanged = (!_fNoShowText != !fNoShowText); _fNoShowText = (fNoShowText != 0); TraceMsg(TF_BAND, "ISFBand::_UpdateShowText turning text %hs", _fNoShowText ? "OFF" : "ON"); if (_hwndTB) { SendMessage(_hwndTB, TB_SETMAXTEXTROWS, _fNoShowText ? 0 : 1, 0L); _UpdateButtons(); } return fChanged; } HRESULT CSFToolbar::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) { BOOL fChanged = FALSE; int idCmd = -1; if (!HIWORD(lpici->lpVerb)) idCmd = LOWORD(lpici->lpVerb); switch (idCmd) { case ISFBIDM_REFRESH: _Refresh(); break; case ISFBIDM_OPEN: OpenFolderPidl(_pidl); break; case ISFBIDM_LARGE: fChanged = _UpdateIconSize(ISFBVIEWMODE_LARGEICONS, TRUE); break; case ISFBIDM_SMALL: fChanged = _UpdateIconSize(ISFBVIEWMODE_SMALLICONS, TRUE); break; case ISFBIDM_SHOWTEXT: fChanged = _UpdateShowText(!_fNoShowText); break; default: if (_pcmSF && idCmd >= _idCmdSF) { LPCSTR lpOldVerb = lpici->lpVerb; lpici->lpVerb = MAKEINTRESOURCEA(idCmd -= _idCmdSF); _pcmSF->InvokeCommand(lpici); _FlushNotifyMessages(_hwndTB); lpici->lpVerb = lpOldVerb; } else TraceMsg(TF_BAND, "SFToolbar::InvokeCommand %d not handled", idCmd); break; } // Our minimum sizes have changed, notify the bandsite // if (fChanged) _ToolbarChanged(); return S_OK; } HRESULT CSFToolbar::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax) { return E_NOTIMPL; } void CSFToolbar::_RegisterToolbar() { // Since _SubclassWindow protects against multiply subclassing, // This call is safe, and ensures that the toolbar is subclassed before // even trying to register it for change notify. if (_hwndTB && _fRegisterChangeNotify) _RegisterChangeNotify(); CDelegateDropTarget::Init(); } void CSFToolbar::_UnregisterToolbar() { if (_hwndTB) { if (_fRegisterChangeNotify) _UnregisterChangeNotify(); _UnsubclassWindow(_hwndTB); } } void CSFToolbar::_RegisterChangeNotify() { // Since we want to register for change notify ONLY once, // and only if this is a file system toolbar. if (!_fFSNRegistered && _fFSNotify) { if (_ptscn) _ptscn->Register(_hwndTB, g_idFSNotify, _lEvents); else _RegisterWindow(_hwndTB, _pidl, _lEvents); _fFSNRegistered = TRUE; } } void CSFToolbar::_UnregisterChangeNotify() { // Only unregister if we have been registered. if (_hwndTB && _fFSNRegistered && _fFSNotify) { _fFSNRegistered = FALSE; if (_ptscn) _ptscn->Unregister(); else _UnregisterWindow(_hwndTB); } } void CSFToolbar::_ReleaseShellFolder() { if (_psf) { IUnknown_SetOwner(_psf, NULL); ATOMICRELEASE(_psf); } ATOMICRELEASE(_ptscn); } // IWinEventHandler::IsWindowOwner HRESULT CSFToolbar::IsWindowOwner(HWND hwnd) { if (hwnd == _hwndTB || hwnd == _hwndToolTips || hwnd == _hwndPager) return S_OK; return S_FALSE; }