//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // itemmenu.cpp // // IConextMenu for folder items. // // History: // // 3/26/97 edwardp Created. // //////////////////////////////////////////////////////////////////////////////// // // Includes // #include "stdinc.h" #include "cdfidl.h" #include "itemmenu.h" #include "dll.h" #include "resource.h" #include // In Shdocvw: shbrowse.cpp #ifndef UNIX extern HRESULT CDDEAuto_Navigate(BSTR str, HWND *phwnd, long lLaunchNewWindow); #else extern "C" HRESULT CDDEAuto_Navigate(BSTR str, HWND *phwnd, long lLaunchNewWindow); #endif /* UNIX */ // // Constructor and destructor. // //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::CContextMenu *** // // Constructor for IContextMenu. // //////////////////////////////////////////////////////////////////////////////// CContextMenu::CContextMenu ( PCDFITEMIDLIST* apcdfidl, LPITEMIDLIST pidlPath, UINT nCount ) : m_cRef(1) { // // Copy the pcdfidls. // ASSERT(apcdfidl || 0 == nCount); ASSERT(NULL == m_apcdfidl); ASSERT(NULL == m_pidlPath); // // In low memory situations pidlPath may be NULL. // if (pidlPath) m_pidlPath = ILClone(pidlPath); IMalloc* pIMalloc; if (SUCCEEDED(SHGetMalloc(&pIMalloc))) { ASSERT(pIMalloc); m_apcdfidl = (PCDFITEMIDLIST*)pIMalloc->Alloc( nCount * sizeof(PCDFITEMIDLIST)); if (m_apcdfidl) { for (UINT i = 0, bOutOfMem = FALSE; (i < nCount) && !bOutOfMem; i++) { ASSERT(CDFIDL_IsValid(apcdfidl[i])); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)apcdfidl[i]))); m_apcdfidl[i] = (PCDFITEMIDLIST)ILClone( (LPITEMIDLIST)apcdfidl[i]); if (bOutOfMem = (NULL == m_apcdfidl[i])) { while (i--) pIMalloc->Free(m_apcdfidl[i]); pIMalloc->Free(m_apcdfidl); m_apcdfidl = NULL; } else { ASSERT(CDFIDL_IsValid(m_apcdfidl[i])); } } } pIMalloc->Release(); } m_nCount = m_apcdfidl ? nCount : 0; // // Don't allow the DLL to unload. // TraceMsg(TF_OBJECTS, "+ IContextMenu"); DllAddRef(); return; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::~CContextMenu *** // // Destructor. // //////////////////////////////////////////////////////////////////////////////// CContextMenu::~CContextMenu ( void ) { ASSERT(0 == m_cRef); // // Free the locally stored cdfidls. // if (m_apcdfidl) { IMalloc* pIMalloc; if (SUCCEEDED(SHGetMalloc(&pIMalloc))) { ASSERT(pIMalloc); while (m_nCount--) { ASSERT(CDFIDL_IsValid(m_apcdfidl[m_nCount])); ASSERT(pIMalloc->DidAlloc(m_apcdfidl[m_nCount])); pIMalloc->Free(m_apcdfidl[m_nCount]); } ASSERT(pIMalloc->DidAlloc(m_apcdfidl)); pIMalloc->Free(m_apcdfidl); pIMalloc->Release(); } } if (m_pidlPath) ILFree(m_pidlPath); // // Matching Release for the constructor Addref. // TraceMsg(TF_OBJECTS, "- IContextMenu"); DllRelease(); return; } // // IUnknown methods. // //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::CContextMenu *** // // CExtractIcon QI. // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CContextMenu::QueryInterface ( REFIID riid, void **ppv ) { ASSERT(ppv); HRESULT hr; *ppv = NULL; if (IID_IUnknown == riid || IID_IContextMenu2 == riid) { *ppv = (IContextMenu2*)this; } else if (IID_IContextMenu == riid) { *ppv = (IContextMenu*)this; } if (*ppv) { ((IUnknown*)*ppv)->AddRef(); hr = S_OK; } else { hr = E_NOINTERFACE; } ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && NULL == *ppv)); return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::AddRef *** // // CContextMenu AddRef. // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CContextMenu::AddRef ( void ) { ASSERT(m_cRef != 0); ASSERT(m_cRef < (ULONG)-1); return ++m_cRef; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::Release *** // // CContextMenu Release. // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CContextMenu::Release ( void ) { ASSERT (m_cRef != 0); ULONG cRef = --m_cRef; if (0 == cRef) delete this; return cRef; } // // IContextMenu methods. // //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::QueryContextMenu *** // // // Description: // Adds menu items to the given item's context menu. // // Parameters: // [In Out] hmenu - A handle to the menu. New items are inserted into // this menu // [In] indexMenu - Zero-based position at which to insert the first // menu item. // [In] idCmdFirst - Minimum value that can be used for a new menu item // identifier. // [In] idCmdLast - Maximum value the can be used for a menu item id. // [In] uFlags - CMF_DEFAULTONLY, CMF_EXPLORE, CMF_NORMAL or // CMF_VERBSONLY. // // Return: // On success the scode contains the the menu identifier offset of the last // menu item added plus one. // // Comments: // CMF_DEFAULTONLY flag indicates the user double-clicked on the item. In // this case no menu is displayed. The shell is simply querying for the ID // of the default action. // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CContextMenu::QueryContextMenu( HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ) { ASSERT(hmenu); ASSERT(idCmdFirst < idCmdLast); HRESULT hr; if (CMF_DEFAULTONLY & uFlags) { ASSERT(idCmdFirst + IDM_OPEN < idCmdLast); InsertMenu(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst + IDM_OPEN, TEXT("")); SetMenuDefaultItem(hmenu, idCmdFirst + IDM_OPEN, FALSE); hr = S_OK; } else { ASSERT(idCmdFirst + IDM_PROPERTIES < idCmdLast); HMENU hmenuParent = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(IDM_CONTEXTMENU)); if (hmenuParent) { HMENU hmenuContext = GetSubMenu(hmenuParent, 0); if (hmenuContext) { ULONG idNew = Shell_MergeMenus(hmenu, hmenuContext, indexMenu, idCmdFirst, idCmdLast, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS); SetMenuDefaultItem(hmenu, idCmdFirst + IDM_OPEN, FALSE); hr = 0x000ffff & idNew; DestroyMenu(hmenuContext); } else { hr = E_FAIL; } DestroyMenu(hmenuParent); } else { hr = E_FAIL; } } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::InvokeCommand *** // // // Description: // Carries out the command for the given menu item id. // // Parameters: // [In] lpici - Structure containing the verb, hwnd, menu id, etc. // // Return: // S_OK if the command was successful. // E_FAIL otherwise. // // Comments: // // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpici ) { ASSERT(lpici); HRESULT hr; if (HIWORD(lpici->lpVerb)) { hr = E_NOTIMPL; } else { WORD wCmd = LOWORD(lpici->lpVerb); switch(wCmd) { case IDM_OPEN: hr = DoOpen(lpici->hwnd, lpici->nShow); break; case IDM_PROPERTIES: hr = DoProperties(lpici->hwnd); break; default: hr = E_NOTIMPL; break; } } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::GetCommandString *** // // // Description: // // // Parameters: // // // Return: // // // Comments: // note -- return an ANSI command string // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CContextMenu::GetCommandString( UINT_PTR idCommand, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax ) { HRESULT hr = E_FAIL; if ((uFlags == GCS_VERB) && (idCommand == IDM_OPEN)) { StrCpyN((LPTSTR)pszName, TEXT("open"), cchMax); hr = NOERROR; } #ifdef UNICODE else if ((uFlags == GCS_VERBA) && (idCommand == IDM_OPEN)) { StrCpyNA(pszName, "open", cchMax); hr = NOERROR; } #endif return hr; } // // IContextMenu2 methods. // //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::HandleMenuMsg *** // // // Description: // // // Parameters: // // // Return: // // // Comments: // // //////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CContextMenu::HandleMenuMsg( UINT uMsg, WPARAM wParam, LPARAM lParam ) { return S_OK; } // // Helper functions. // //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::DoOpen *** // // // Description: // Command handler for IDM_OPEN. // // Parameters: // [In] hwnd - Parent window. Used for dialogs etc. // [In] nShow - ShowFlag use in ShowWindow command. // // Return: // S_OK if the command executed. // E_FAIL if the command iddn't execute. // E_OUTOFMEMORY if there wasn't enough memory to execute the command. // // Comments: // // //////////////////////////////////////////////////////////////////////////////// HRESULT CContextMenu::DoOpen( HWND hwnd, int nShow ) { HRESULT hr; if (m_apcdfidl) { ASSERT(CDFIDL_IsValid(m_apcdfidl[0])); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)m_apcdfidl[0]))); if (CDFIDL_IsFolder(m_apcdfidl[0])) { hr = DoOpenFolder(hwnd, nShow); } else { hr = DoOpenStory(hwnd, nShow); } } else { hr = E_OUTOFMEMORY; } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::DoOpenFolder *** // // // Description: // Open command for folders. // // Parameters: // [In] hwnd - Parent window. Used for dialogs etc. // [In] nShow - ShowFlag use in ShowWindow command. // // Return: // S_OK if the command executed. // E_FAIL if the command iddn't execute. // E_OUTOFMEMORY if there wasn't enough memory to execute the command. // // Comments: // // //////////////////////////////////////////////////////////////////////////////// HRESULT CContextMenu::DoOpenFolder( HWND hwnd, int nShow ) { HRESULT hr; if (m_pidlPath) { ASSERT(m_apcdfidl); ASSERT(CDFIDL_IsValid(m_apcdfidl[0])); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)m_apcdfidl[0]))); LPITEMIDLIST pidlFull = ILCombine(m_pidlPath, (LPITEMIDLIST)m_apcdfidl[0]); if (pidlFull) { SHELLEXECUTEINFO ei = {0}; ei.cbSize = sizeof(SHELLEXECUTEINFO); ei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME; ei.hwnd = hwnd; ei.lpVerb = TEXT("Open"); ei.nShow = nShow; ei.lpIDList = (LPVOID)pidlFull; ei.lpClass = TEXT("Folder"); hr = ShellExecuteEx(&ei) ? S_OK : E_FAIL; ILFree(pidlFull); } else { hr = E_OUTOFMEMORY; } } else { hr = E_OUTOFMEMORY; } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::DoOpenStory *** // // // Description: // Open command for stories (internet links). // // Parameters: // [In] hwnd - Parent window. Used for dialogs etc. // [In] nShow - ShowFlag use in ShowWindow command. // // Return: // S_OK if ShellExecuteEx succeeded. // E_FAIL if ShellExecuteEx didn't succeed. // // Comments: // // //////////////////////////////////////////////////////////////////////////////// HRESULT CContextMenu::DoOpenStory( HWND hwnd, int nShow ) { ASSERT(m_apcdfidl); ASSERT(CDFIDL_IsValid(m_apcdfidl[0])); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)m_apcdfidl[0]))); HRESULT hr = E_FAIL; LPTSTR pszURL = CDFIDL_GetURL(m_apcdfidl[0]); if (PathIsURL(pszURL)) { WCHAR wszURL[INTERNET_MAX_URL_LENGTH]; HWND hwndTemp = (HWND)-1; BSTR bstrURL; SHTCharToUnicode(pszURL, wszURL, ARRAYSIZE(wszURL)); bstrURL = SysAllocString(wszURL); if (bstrURL) { hr = CDDEAuto_Navigate(bstrURL, &hwndTemp, 0); SysFreeString(bstrURL); } else { hr = E_OUTOFMEMORY; } } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CContextMenu::DoProperties *** // // // Description: // Command handler for IDM_PROPERTIES. // // Parameters: // [In] hwnd - Parent window. Used for dialogs etc. // // Return: // S_OK if the command executed. // E_FAIL if the command iddn't execute. // E_OUTOFMEMORY if there wasn't enough memory to execute the command. // // Comments: // Uses the property pages of the InternetShortcut object. // //////////////////////////////////////////////////////////////////////////////// HRESULT CContextMenu::DoProperties( HWND hwnd ) { HRESULT hr; if (m_apcdfidl) { ASSERT(CDFIDL_IsValid(m_apcdfidl[0])); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)m_apcdfidl[0]))); IShellPropSheetExt* pIShellPropSheetExt; hr = QueryInternetShortcut(m_apcdfidl[0], IID_IShellPropSheetExt, (void**)&pIShellPropSheetExt); if (SUCCEEDED(hr)) { ASSERT(pIShellPropSheetExt); PROPSHEETHEADER psh = {0}; HPROPSHEETPAGE ahpage[MAX_PROP_PAGES]; psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = PSH_NOAPPLYNOW; psh.hwndParent = hwnd; psh.hInstance = MLGetHinst(); psh.pszCaption = CDFIDL_GetName(m_apcdfidl[0]); psh.phpage = ahpage; hr = pIShellPropSheetExt->AddPages(AddPages_Callback, (LPARAM)&psh); if (SUCCEEDED(hr)) { // // Property sheets are currently disabled. This is the only // API called in comctl32.dll so remove it to avoid a //dependency. //hr = (-1 == PropertySheet(&psh)) ? E_FAIL : S_OK; } pIShellPropSheetExt->Release(); } } else { hr = E_OUTOFMEMORY; } return hr; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** AddPages_Callback *** // // // Description: // // // Parameters: // // // Return: // // // Comments: // // //////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK AddPages_Callback( HPROPSHEETPAGE hpage, LPARAM lParam ) { ASSERT(hpage); ASSERT(lParam); BOOL bRet; PROPSHEETHEADER* ppsh = (PROPSHEETHEADER*)lParam; if (ppsh->nPages < MAX_PROP_PAGES) { ppsh->phpage[ppsh->nPages++] = hpage; bRet = TRUE; } else { bRet = FALSE; } return bRet; } // // // HRESULT CALLBACK MenuCallBack( IShellFolder* pIShellFolder, HWND hwndOwner, LPDATAOBJECT pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return S_OK; } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // // *** CCdfView::QueryInternetShortcut *** // // // Description: // Sets up an internet shorcut object for the given pidl. // // Parameters: // [In] pcdfidl - The shortcut object is created for the URL stored in this // cdf item id list. // [In] riid - The requested interface on the shortcut object. // [Out] ppvOut - A pointer that receives the interface. // // Return: // S_OK if the object is created and the interface is found. // A COM error code otherwise. // // Comments: // // //////////////////////////////////////////////////////////////////////////////// HRESULT CContextMenu::QueryInternetShortcut( PCDFITEMIDLIST pcdfidl, REFIID riid, void** ppvOut ) { ASSERT(CDFIDL_IsValid(pcdfidl)); ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)pcdfidl))); ASSERT(ppvOut); HRESULT hr; *ppvOut = NULL; // // Only create a shell link object if the CDF contains an URL // if (*(CDFIDL_GetURL(pcdfidl)) != 0) { IShellLinkA * pIShellLink; hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, (void**)&pIShellLink); if (SUCCEEDED(hr)) { ASSERT(pIShellLink); #ifdef UNICODE CHAR szUrlA[INTERNET_MAX_URL_LENGTH]; SHTCharToAnsi(CDFIDL_GetURL(pcdfidl), szUrlA, ARRAYSIZE(szUrlA)); hr = pIShellLink->SetPath(szUrlA); #else hr = pIShellLink->SetPath(CDFIDL_GetURL(pcdfidl)); #endif if (SUCCEEDED(hr)) { // // The description ends up being the file name created. // TCHAR szPath[MAX_PATH]; #ifdef UNICODE CHAR szPathA[MAX_PATH]; #endif StrCpyN(szPath, CDFIDL_GetName(pcdfidl), ARRAYSIZE(szPath) - 5); StrCat(szPath, TEXT(".url")); #ifdef UNICODE SHTCharToAnsi(szPath, szPathA, ARRAYSIZE(szPathA)); pIShellLink->SetDescription(szPathA); #else pIShellLink->SetDescription(szPath); #endif hr = pIShellLink->QueryInterface(riid, ppvOut); } pIShellLink->Release(); } } else { hr = E_FAIL; } ASSERT((SUCCEEDED(hr) && *ppvOut) || (FAILED(hr) && NULL == *ppvOut)); return hr; }