//******************************************************************************************* // // Filename : SFVWnd.cpp // // Implementation file for CSFVDropSource and CSFViewDlg // // Copyright (c) 1994 - 1996 Microsoft Corporation. All rights reserved // //******************************************************************************************* #include "Pch.H" #include "SFVWnd.H" #include "ThisDll.h" #include "Resource.H" class CSFVDropSource : public CUnknown, public IDropSource { public: CSFVDropSource() : m_grfInitialKeyState(0) {} virtual ~CSFVDropSource(); STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState); STDMETHODIMP GiveFeedback(DWORD dwEffect); private: DWORD m_grfInitialKeyState; } ; CSFVDropSource::~CSFVDropSource() { } STDMETHODIMP CSFVDropSource::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const IID *apiid[] = { &IID_IDropSource, NULL }; LPUNKNOWN aobj[] = { (IDropSource *)this }; return(QIHelper(riid, ppvObj, apiid, aobj)); } STDMETHODIMP_(ULONG) CSFVDropSource::AddRef() { return(AddRefHelper()); } STDMETHODIMP_(ULONG) CSFVDropSource::Release() { return(ReleaseHelper()); } STDMETHODIMP CSFVDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { if (fEscapePressed) { return(DRAGDROP_S_CANCEL); } // initialize ourself with the drag begin button if (m_grfInitialKeyState == 0) { m_grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)); } if (!(grfKeyState & m_grfInitialKeyState)) { return(DRAGDROP_S_DROP); } return(S_OK); } STDMETHODIMP CSFVDropSource::GiveFeedback(DWORD dwEffect) { return(DRAGDROP_S_USEDEFAULTCURSORS); } // Note that the OLESTR gets freed, so don't try to use it later BOOL StrRetToStr(LPSTR szOut, UINT uszOut, LPSTRRET pStrRet, LPCITEMIDLIST pidl) { switch (pStrRet->uType) { case STRRET_OLESTR: { CSafeMalloc sm; OleStrToStrN(szOut, uszOut, pStrRet->pOleStr, -1); sm.Free(pStrRet->pOleStr); break; } case STRRET_CSTR: lstrcpyn(szOut, pStrRet->cStr, uszOut); break; case STRRET_OFFSET: if (pidl) { lstrcpyn(szOut, ((LPCSTR)&pidl->mkid)+pStrRet->uOffset, uszOut); break; } // Fall through default: if (uszOut) { *szOut = '\0'; } return(FALSE); } return(TRUE); } static void _PrettyMenu(HMENU hm) { BOOL bSeparated = TRUE; int i; for (i=GetMenuItemCount(hm)-1; i>0; --i) { if (CSFViewDlg::IsMenuSeparator(hm, i)) { if (bSeparated) { DeleteMenu(hm, i, MF_BYPOSITION); } bSeparated = TRUE; } else { bSeparated = FALSE; } } // The above loop does not handle the case of many separators at // the beginning of the menu while (CSFViewDlg::IsMenuSeparator(hm, 0)) { DeleteMenu(hm, 0, MF_BYPOSITION); } } static HMENU _LoadPopupMenu(UINT id, UINT uSubMenu) { HMENU hmParent = LoadMenu(g_ThisDll.GetInstance(), MAKEINTRESOURCE(id)); if (!hmParent) { return(NULL); } HMENU hmPopup = GetSubMenu(hmParent, 0); RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION); DestroyMenu(hmParent); return(hmPopup); } void CSFViewDlg::InitDialog() { m_cList.Init(GetDlgItem(m_hDlg, IDC_LISTVIEW), GetDlgItem(m_hDlg, IDC_LISTBOX), IDI_GENERIC); SetWindowLong(m_cList, GWL_EXSTYLE, GetWindowLong(m_cList, GWL_EXSTYLE) | WS_EX_CLIENTEDGE); m_hrOLE = OleInitialize(NULL); } LRESULT CSFViewDlg::BeginDrag() { if (!OleInited()) { return(0); } // Get the dwEffect from the selection. ULONG dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY; GetAttributesFromItem(&dwEffect, SVGIO_SELECTION); // Just in case dwEffect &= DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY; if (!dwEffect) { return(0); } LPDATAOBJECT pdtobj; if (FAILED(GetUIObjectFromItem(IID_IDataObject, (LPVOID*)&pdtobj, SVGIO_SELECTION))) { return(0); } CEnsureRelease erData(pdtobj); CSFVDropSource *pcsrc = new CSFVDropSource; if (!pcsrc) { return(0); } pcsrc->AddRef(); CEnsureRelease erCDSrc((IDropSource*)pcsrc); IDropSource *pdsrc; HRESULT hres = pcsrc->QueryInterface(IID_IDropSource, (LPVOID*)&pdsrc); if (FAILED(hres)) { return(0); } CEnsureRelease erPDSrc(pdsrc); DoDragDrop(pdtobj, pdsrc, dwEffect, &dwEffect); return(0); } BOOL CSFViewDlg::Notify(LPNMHDR pNotify) { LPNM_LISTVIEW pNotifyLV = (LPNM_LISTVIEW)pNotify; LV_DISPINFO *pNotifyDI = (LV_DISPINFO *)pNotify; LPTBNOTIFY pNotifyTB = (LPTBNOTIFY)pNotify; LPTOOLTIPTEXT pNotifyTT = (LPTOOLTIPTEXT)pNotify; switch(pNotify->code) { case TTN_NEEDTEXT: m_psfv->GetCommandHelpText(pNotifyTT->hdr.idFrom, pNotifyTT->szText, sizeof(pNotifyTT->szText), TRUE); break; case TBN_BEGINDRAG: m_psfv->OnMenuSelect(pNotifyTB->iItem, 0, 0); break; case LVN_DELETEITEM: m_psfv->m_cMalloc.Free((LPITEMIDLIST)pNotifyLV->lParam); break; case LVN_GETDISPINFO: if (pNotifyDI->item.iSubItem == 0) { LPCITEMIDLIST pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam; if (pNotifyDI->item.mask & LVIF_TEXT) { STRRET strret; if (FAILED(m_psfv->m_psf->GetDisplayNameOf(pidl, 0, &strret))) { lstrcpyn(pNotifyDI->item.pszText, "", pNotifyDI->item.cchTextMax); } else { StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax, &strret, pidl); } } if (pNotifyDI->item.mask & LVIF_IMAGE) { // Get the image pNotifyDI->item.iImage = m_cList.GetIcon(m_psfv->m_psf, pidl); } pNotifyDI->item.mask |= LVIF_DI_SETITEM; } else if (pNotifyDI->item.mask & LVIF_TEXT) { SFVCB_GETDETAILSOF_DATA gdo; gdo.pidl = (LPCITEMIDLIST)pNotifyDI->item.lParam; if (m_psfv->CallCB(SFVCB_GETDETAILSOF, pNotifyDI->item.iSubItem, (LPARAM)&gdo) == S_OK) { StrRetToStr(pNotifyDI->item.pszText, pNotifyDI->item.cchTextMax, &gdo.str, gdo.pidl); } } break; case LVN_COLUMNCLICK: m_psfv->ColumnClick(pNotifyLV->iSubItem); break; case LVN_ITEMCHANGED: // We only care about STATECHANGE messages if (!(pNotifyLV->uChanged & LVIF_STATE)) { // If the text is changed, we need to flush the cached // context menu. if (pNotifyLV->uChanged & LVIF_TEXT) { m_psfv->ReleaseSelContextMenu(); } break; } // tell commdlg that selection may have changed m_psfv->OnStateChange(CDBOSC_SELCHANGE); // The rest only cares about SELCHANGE messages if ((pNotifyLV->uNewState^pNotifyLV->uOldState) & LVIS_SELECTED) { m_psfv->ReleaseSelContextMenu(); } break; case LVN_BEGINDRAG: case LVN_BEGINRDRAG: return(BeginDrag()); case NM_RETURN: case NM_DBLCLK: ContextMenu((DWORD)-1, TRUE); break; default: return(FALSE); } return(TRUE); } void CSFViewDlg::ContextMenu(DWORD dwPos, BOOL bDoDefault) { int idDefault = -1; int nInsert; UINT fFlags = 0; POINT pt; // Find the selected item int iItem = ListView_GetNextItem(m_cList, -1, LVNI_SELECTED); if (dwPos == (DWORD) -1) { if (iItem != -1) { RECT rc; int iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED|LVNI_SELECTED); if (iItemFocus == -1) { iItemFocus = iItem; } // Note that ListView_GetItemRect returns client coordinates ListView_GetItemRect(m_cList, iItemFocus, &rc, LVIR_ICON); pt.x = (rc.left+rc.right)/2; pt.y = (rc.top+rc.bottom)/2; } else { pt.x = pt.y = 0; } MapWindowPoints(m_cList, HWND_DESKTOP, &pt, 1); } else { pt.x = GET_X_LPARAM(dwPos); pt.y = GET_Y_LPARAM(dwPos); } CMenuTemp cmContext(CreatePopupMenu()); if (!(HMENU)cmContext) { // There should be an error message here return; } LPCONTEXTMENU pcm = NULL; if (iItem == -1) { // No selected item; use the background context menu nInsert = -1; CMenuTemp cmMerge(_LoadPopupMenu(MENU_SFV, 0)); if (!(HMENU)cmMerge) { // There should be an error message here return; } Cab_MergeMenus(cmContext, cmMerge, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST, 0); m_psfv->MergeArrangeMenu(cmContext); m_psfv->InitViewMenu(cmContext); if (FAILED(m_psfv->m_psf->CreateViewObject(m_psfv->m_hwndMain, IID_IContextMenu, (LPVOID *)&pcm))) { pcm = NULL; } } else { nInsert = 0; pcm = m_psfv->GetSelContextMenu(); } CEnsureRelease erPCM(pcm); if (pcm) { if (m_psfv->m_psb) { // Determine whether we are in Explorer mode HWND hwnd = NULL; m_psfv->m_psb->GetControlWindow(FCW_TREE, &hwnd); if (hwnd) { fFlags |= CMF_EXPLORE; } } pcm->QueryContextMenu(cmContext, nInsert, SFV_CONTEXT_FIRST, SFV_CONTEXT_LAST, fFlags); // If this is the common dialog browser, we need to make the // default command "Select" so that double-clicking (which is // open in common dialog) makes sense. if (m_psfv->IsInCommDlg() && iItem!=-1) { // make sure this is an item CMenuTemp cmSelect(_LoadPopupMenu(MENU_SFV, 1)); Cab_MergeMenus(cmContext, cmSelect, 0, 0, (UINT)-1, MM_ADDSEPARATOR); SetMenuDefaultItem(cmContext, 0, MF_BYPOSITION); } idDefault = GetMenuDefaultItem(cmContext, MF_BYCOMMAND, 0); } _PrettyMenu(cmContext); int idCmd; if (bDoDefault) { if (idDefault < 0) { return; } idCmd = idDefault; } else { idCmd = TrackPopupMenu(cmContext, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pt.x, pt.y, 0, m_psfv->m_cView, NULL); } if ((idCmd==idDefault) && m_psfv->OnDefaultCommand()==S_OK) { // commdlg browser ate the default command } else if (idCmd == 0) { // No item selected } else { m_psfv->OnCommand(pcm, GET_WM_COMMAND_MPS(idCmd, 0, 0)); } } BOOL CSFViewDlg::RealDlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: InitDialog(); break; case WM_DESTROY: m_cList.DeleteAllItems(); if (OleInited()) { OleUninitialize(); m_hrOLE = E_UNEXPECTED; } break; case WM_NOTIFY: SetWindowLong(m_hDlg, DWL_MSGRESULT, Notify((LPNMHDR)lParam)); break; case WM_INITMENUPOPUP: m_psfv->OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam)); break; case WM_COMMAND: m_psfv->OnCommand(NULL, wParam, lParam); break; case WM_CONTEXTMENU: ContextMenu(lParam); break; case WM_SIZE: SetWindowPos(m_cList, NULL, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), SWP_NOZORDER|SWP_SHOWWINDOW); break; case WM_MENUSELECT: m_psfv->OnMenuSelect(GET_WM_MENUSELECT_CMD(wParam, lParam), GET_WM_MENUSELECT_FLAGS(wParam, lParam), GET_WM_MENUSELECT_HMENU(wParam, lParam)); break; default: return(FALSE); } return(TRUE); } UINT CSFViewDlg::CharWidth() { HDC hdc = GetDC(m_cList); SelectFont(hdc, FORWARD_WM_GETFONT(m_cList, SendMessage)); SIZE siz; GetTextExtentPoint(hdc, "0", 1, &siz); ReleaseDC(m_cList, hdc); return(siz.cx); } int CSFViewDlg::AddObject(LPCITEMIDLIST pidl) { LV_ITEM item; item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; item.iItem = 0x7fff; // add at end item.iSubItem = 0; item.iImage = I_IMAGECALLBACK; item.pszText = LPSTR_TEXTCALLBACK; item.lParam = (LPARAM)pidl; return(m_cList.InsertItem(&item)); } LPCITEMIDLIST CSFViewDlg::GetPIDL(int iItem) { LV_ITEM item; item.mask = LVIF_PARAM; item.iItem = iItem; item.iSubItem = 0; item.lParam = 0; if (iItem != -1) { ListView_GetItem(m_cList, &item); } return((LPCITEMIDLIST)item.lParam); } UINT CSFViewDlg::GetItemPIDLS(LPCITEMIDLIST apidl[], UINT cItemMax, UINT uItem) { // We should put the focused one at the top of the list. int iItem = -1; int iItemFocus = -1; UINT cItem = 0; UINT uType; switch (uItem) { case SVGIO_SELECTION: // special case for faster search if (!cItemMax) { return ListView_GetSelectedCount(m_cList); } iItemFocus = ListView_GetNextItem(m_cList, -1, LVNI_FOCUSED); uType = LVNI_SELECTED; break; case SVGIO_ALLVIEW: // special case for faster search if (!cItemMax) { return ListView_GetItemCount(m_cList); } uType = LVNI_ALL; break; default: return(0); } while((iItem=ListView_GetNextItem(m_cList, iItem, uType)) != -1) { if (cItem < cItemMax) { // Check if the item is the focused one or not. if (iItem == iItemFocus) { // Yes, put it at the top. apidl[cItem] = apidl[0]; apidl[0] = GetPIDL(iItem); } else { // No, put it at the end of the list. apidl[cItem] = GetPIDL(iItem); } } cItem++; } return cItem; } HRESULT CSFViewDlg::GetItemObjects(LPCITEMIDLIST **ppidl, UINT uItem) { UINT cItems = GetItemPIDLS(NULL, 0, uItem); LPCITEMIDLIST * apidl; if (ppidl != NULL) { *ppidl = NULL; if (cItems == 0) { return(ResultFromShort(0)); // nothing allocated... } apidl = new LPCITEMIDLIST[cItems]; if (!apidl) { return(E_OUTOFMEMORY); } *ppidl = apidl; cItems = GetItemPIDLS(apidl, cItems, uItem); } return(ResultFromShort(cItems)); } HRESULT CSFViewDlg::GetUIObjectFromItem(REFIID riid, LPVOID * ppv, UINT uItem) { LPCITEMIDLIST * apidl; HRESULT hres = GetItemObjects(&apidl, uItem); UINT cItems = ShortFromResult(hres); if (FAILED(hres)) { return(hres); } if (!cItems) { return(E_INVALIDARG); } hres = m_psfv->m_psf->GetUIObjectOf(m_psfv->m_hwndMain, cItems, apidl, riid, 0, ppv); delete apidl; return hres; } HRESULT CSFViewDlg::GetAttributesFromItem(ULONG *pdwAttr, UINT uItem) { LPCITEMIDLIST * apidl; HRESULT hres = GetItemObjects(&apidl, uItem); UINT cItems = ShortFromResult(hres); if (FAILED(hres)) { return(hres); } if (!cItems) { return(E_INVALIDARG); } hres = m_psfv->m_psf->GetAttributesOf(cItems, apidl, pdwAttr); delete apidl; return hres; } BOOL CSFViewDlg::IsMenuSeparator(HMENU hm, int i) { MENUITEMINFO mii; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE; mii.cch = 0; // WARNING: We MUST initialize it to 0!!! if (!GetMenuItemInfo(hm, i, TRUE, &mii)) { return(FALSE); } if (mii.fType & MFT_SEPARATOR) { return(TRUE); } return(FALSE); }