joachim99@77: /*************************************************************************** joachim99@77: ShellContextMenu.cpp - description joachim99@77: ------------------- joachim99@77: begin : Sat Mar 4 2006 joachim99@77: copyright : (C) 2005-2007 by Joachim Eibl joachim99@77: email : joachim dot eibl at gmx dot de joachim99@77: ***************************************************************************/ joachim99@77: joachim99@77: /*************************************************************************** joachim99@77: * * joachim99@77: * This program is free software; you can redistribute it and/or modify * joachim99@77: * it under the terms of the GNU General Public License as published by * joachim99@77: * the Free Software Foundation; either version 2 of the License, or * joachim99@77: * (at your option) any later version. * joachim99@77: * * joachim99@77: ***************************************************************************/ joachim99@69: // ShellContextMenu.cpp: Implementierung der Klasse CShellContextMenu. joachim99@69: // joachim99@69: ////////////////////////////////////////////////////////////////////// joachim99@69: #ifdef _WIN32 joachim99@69: #include joachim99@69: #include joachim99@69: #include joachim99@69: #include joachim99@69: #include joachim99@69: #include joachim99@69: #include joachim99@70: #include joachim99@69: #include "ShellContextMenu.h" joachim99@69: joachim99@69: #ifdef _DEBUG joachim99@69: #undef THIS_FILE joachim99@69: static char THIS_FILE[]=__FILE__; joachim99@69: #define new DEBUG_NEW joachim99@69: #endif joachim99@69: joachim99@69: ////////////////////////////////////////////////////////////////////// joachim99@69: // Konstruktion/Destruktion joachim99@69: ////////////////////////////////////////////////////////////////////// joachim99@69: joachim99@69: #define MIN_ID 100 joachim99@69: #define MAX_ID 10000 joachim99@69: joachim99@69: joachim99@70: void showShellContextMenu( const QString& itemPath, QPoint pt, QWidget* pParentWidget, QMenu* pMenu ) joachim99@69: { joachim99@69: CShellContextMenu scm; joachim99@77: scm.SetObjects(QDir::toNativeSeparators(QDir::cleanPath(itemPath))); joachim99@69: int id = scm.ShowContextMenu (pParentWidget, pt, pMenu); joachim99@69: if (id>=1) joachim99@75: pMenu->actions().value(id-1)->trigger(); joachim99@69: } joachim99@69: joachim99@69: IContextMenu2 * g_IContext2 = NULL; joachim99@69: IContextMenu3 * g_IContext3 = NULL; joachim99@69: joachim99@69: CShellContextMenu::CShellContextMenu() joachim99@69: { joachim99@69: m_psfFolder = NULL; joachim99@69: m_pidlArray = NULL; joachim99@69: m_hMenu = NULL; joachim99@69: } joachim99@69: joachim99@69: CShellContextMenu::~CShellContextMenu() joachim99@69: { joachim99@69: // free all allocated datas joachim99@69: if (m_psfFolder && bDelete) joachim99@69: m_psfFolder->Release (); joachim99@69: m_psfFolder = NULL; joachim99@69: FreePIDLArray (m_pidlArray); joachim99@69: m_pidlArray = NULL; joachim99@69: joachim99@69: if (m_hMenu) joachim99@69: DestroyMenu( m_hMenu ); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: joachim99@69: // this functions determines which version of IContextMenu is avaibale for those objects (always the highest one) joachim99@69: // and returns that interface joachim99@69: BOOL CShellContextMenu::GetContextMenu (void ** ppContextMenu, int & iMenuType) joachim99@69: { joachim99@69: *ppContextMenu = NULL; joachim99@69: LPCONTEXTMENU icm1 = NULL; joachim99@77: joachim99@77: if ( m_psfFolder==0 ) joachim99@77: return FALSE; joachim99@69: joachim99@69: // first we retrieve the normal IContextMenu interface (every object should have it) joachim99@69: m_psfFolder->GetUIObjectOf (NULL, nItems, (LPCITEMIDLIST *) m_pidlArray, IID_IContextMenu, NULL, (void**) &icm1); joachim99@69: joachim99@69: if (icm1) joachim99@69: { // since we got an IContextMenu interface we can now obtain the higher version interfaces via that joachim99@69: if (icm1->QueryInterface (IID_IContextMenu3, ppContextMenu) == NOERROR) joachim99@69: iMenuType = 3; joachim99@69: else if (icm1->QueryInterface (IID_IContextMenu2, ppContextMenu) == NOERROR) joachim99@69: iMenuType = 2; joachim99@69: joachim99@69: if (*ppContextMenu) joachim99@69: icm1->Release(); // we can now release version 1 interface, cause we got a higher one joachim99@69: else joachim99@69: { joachim99@69: iMenuType = 1; joachim99@69: *ppContextMenu = icm1; // since no higher versions were found joachim99@69: } // redirect ppContextMenu to version 1 interface joachim99@69: } joachim99@69: else joachim99@69: return (FALSE); // something went wrong joachim99@69: joachim99@69: return (TRUE); // success joachim99@69: } joachim99@69: joachim99@69: joachim99@69: LRESULT CALLBACK CShellContextMenu::HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) joachim99@69: { joachim99@69: switch (message) joachim99@69: { joachim99@69: case WM_MENUCHAR: // only supported by IContextMenu3 joachim99@69: if (g_IContext3) joachim99@69: { joachim99@69: LRESULT lResult = 0; joachim99@69: g_IContext3->HandleMenuMsg2 (message, wParam, lParam, &lResult); joachim99@69: return (lResult); joachim99@69: } joachim99@69: break; joachim99@69: joachim99@69: case WM_DRAWITEM: joachim99@69: case WM_MEASUREITEM: joachim99@69: if (wParam) joachim99@69: break; // if wParam != 0 then the message is not menu-related joachim99@69: joachim99@69: case WM_INITMENUPOPUP: joachim99@69: if (g_IContext2) joachim99@69: g_IContext2->HandleMenuMsg (message, wParam, lParam); joachim99@69: else // version 3 joachim99@69: g_IContext3->HandleMenuMsg (message, wParam, lParam); joachim99@69: return (message == WM_INITMENUPOPUP ? 0 : TRUE); // inform caller that we handled WM_INITPOPUPMENU by ourself joachim99@69: break; joachim99@69: joachim99@69: default: joachim99@69: break; joachim99@69: } joachim99@69: joachim99@69: // call original WndProc of window to prevent undefined bevhaviour of window joachim99@69: return ::CallWindowProc ((WNDPROC) GetProp ( hWnd, TEXT ("OldWndProc")), hWnd, message, wParam, lParam); joachim99@69: } joachim99@69: joachim99@69: joachim99@70: UINT CShellContextMenu::ShowContextMenu(QWidget * pParentWidget, QPoint pt, QMenu* pMenu ) joachim99@69: { Chris@112: HWND hWnd = (HWND)pParentWidget->winId(); joachim99@69: int iMenuType = 0; // to know which version of IContextMenu is supported joachim99@69: LPCONTEXTMENU pContextMenu; // common pointer to IContextMenu and higher version interface joachim99@69: joachim99@69: if (!GetContextMenu ((void**) &pContextMenu, iMenuType)) joachim99@69: return (0); // something went wrong joachim99@69: joachim99@69: if (!m_hMenu) joachim99@69: { joachim99@69: DestroyMenu( m_hMenu ); joachim99@69: m_hMenu = CreatePopupMenu (); joachim99@69: } joachim99@69: joachim99@75: int i; joachim99@75: QList actionList = pMenu->actions(); joachim99@75: for( i=0; itext(); joachim99@69: if (!s.isEmpty()) joachim99@75: AppendMenuW( m_hMenu, MF_STRING, i+1, (LPCWSTR)s.utf16() ); joachim99@69: } joachim99@69: AppendMenuW( m_hMenu, MF_SEPARATOR, i+1, L"" ); joachim99@69: joachim99@69: // lets fill the our popupmenu joachim99@69: pContextMenu->QueryContextMenu (m_hMenu, GetMenuItemCount (m_hMenu), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE); joachim99@69: joachim99@69: // subclass window to handle menurelated messages in CShellContextMenu joachim99@69: WNDPROC OldWndProc; joachim99@69: if (iMenuType > 1) // only subclass if its version 2 or 3 joachim99@69: { joachim99@69: OldWndProc = (WNDPROC) SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) HookWndProc); joachim99@69: if (iMenuType == 2) joachim99@69: g_IContext2 = (LPCONTEXTMENU2) pContextMenu; joachim99@69: else // version 3 joachim99@69: g_IContext3 = (LPCONTEXTMENU3) pContextMenu; joachim99@69: } joachim99@69: else joachim99@69: OldWndProc = NULL; joachim99@69: Chris@112: UINT idCommand = TrackPopupMenu (m_hMenu,TPM_RETURNCMD | TPM_LEFTALIGN, pt.x(), pt.y(), 0, (HWND)pParentWidget->winId(), 0); joachim99@69: joachim99@69: if (OldWndProc) // unsubclass joachim99@69: SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) OldWndProc); joachim99@69: joachim99@69: if (idCommand >= MIN_ID && idCommand <= MAX_ID) // see if returned idCommand belongs to shell menu entries joachim99@69: { joachim99@69: InvokeCommand (pContextMenu, idCommand - MIN_ID); // execute related command joachim99@69: idCommand = 0; joachim99@69: } joachim99@69: joachim99@69: pContextMenu->Release(); joachim99@69: g_IContext2 = NULL; joachim99@69: g_IContext3 = NULL; joachim99@69: joachim99@69: return (idCommand); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: void CShellContextMenu::InvokeCommand (LPCONTEXTMENU pContextMenu, UINT idCommand) joachim99@69: { joachim99@69: CMINVOKECOMMANDINFO cmi = {0}; joachim99@69: cmi.cbSize = sizeof (CMINVOKECOMMANDINFO); joachim99@69: cmi.lpVerb = (LPSTR) MAKEINTRESOURCE (idCommand); joachim99@69: cmi.nShow = SW_SHOWNORMAL; joachim99@69: joachim99@69: pContextMenu->InvokeCommand (&cmi); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: void CShellContextMenu::SetObjects(const QString& strObject) joachim99@69: { joachim99@69: // only one object is passed joachim99@69: QStringList strArray; joachim99@69: strArray << strObject; // create a CStringArray with one element joachim99@69: joachim99@69: SetObjects (strArray); // and pass it to SetObjects (CStringArray &strArray) joachim99@69: // for further processing joachim99@69: } joachim99@69: joachim99@69: joachim99@69: void CShellContextMenu::SetObjects(const QStringList &strList) joachim99@69: { joachim99@69: // free all allocated datas joachim99@69: if (m_psfFolder && bDelete) joachim99@69: m_psfFolder->Release (); joachim99@69: m_psfFolder = NULL; joachim99@69: FreePIDLArray (m_pidlArray); joachim99@69: m_pidlArray = NULL; joachim99@69: joachim99@69: // get IShellFolder interface of Desktop (root of shell namespace) joachim99@69: IShellFolder * psfDesktop = NULL; joachim99@69: SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl joachim99@69: joachim99@69: // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface joachim99@69: // but since we use the Desktop as our interface and the Desktop is the namespace root joachim99@69: // that means that it's a fully qualified PIDL, which is what we need joachim99@69: LPITEMIDLIST pidl = NULL; joachim99@69: joachim99@75: psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[0].utf16(), NULL, &pidl, NULL); joachim99@69: joachim99@69: // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface joachim99@69: LPITEMIDLIST pidlItem = NULL; // relative pidl joachim99@69: SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL); joachim99@69: free (pidlItem); joachim99@69: // get interface to IMalloc (need to free the PIDLs allocated by the shell functions) joachim99@69: LPMALLOC lpMalloc = NULL; joachim99@69: SHGetMalloc (&lpMalloc); joachim99@69: lpMalloc->Free (pidl); joachim99@69: joachim99@69: // now we have the IShellFolder interface to the parent folder specified in the first element in strArray joachim99@69: // since we assume that all objects are in the same folder (as it's stated in the MSDN) joachim99@69: // we now have the IShellFolder interface to every objects parent folder joachim99@69: joachim99@69: IShellFolder * psfFolder = NULL; joachim99@69: nItems = strList.size (); joachim99@69: for (int i = 0; i < nItems; i++) joachim99@69: { joachim99@69: pidl=0; joachim99@75: psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[i].utf16(), NULL, &pidl, NULL); joachim99@69: if (pidl) joachim99@69: { joachim99@69: m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST)); joachim99@69: // get relative pidl via SHBindToParent joachim99@69: SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem); joachim99@69: m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray joachim99@69: free (pidlItem); joachim99@69: lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName joachim99@69: psfFolder->Release (); joachim99@69: } joachim99@69: } joachim99@69: lpMalloc->Release (); joachim99@69: psfDesktop->Release (); joachim99@69: joachim99@69: bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu joachim99@69: } joachim99@69: joachim99@69: joachim99@69: // only one full qualified PIDL has been passed joachim99@69: void CShellContextMenu::SetObjects(LPITEMIDLIST /*pidl*/) joachim99@69: { joachim99@69: /* joachim99@69: // free all allocated datas joachim99@69: if (m_psfFolder && bDelete) joachim99@69: m_psfFolder->Release (); joachim99@69: m_psfFolder = NULL; joachim99@69: FreePIDLArray (m_pidlArray); joachim99@69: m_pidlArray = NULL; joachim99@69: joachim99@69: // full qualified PIDL is passed so we need joachim99@69: // its parent IShellFolder interface and its relative PIDL to that joachim99@69: LPITEMIDLIST pidlItem = NULL; joachim99@69: SHBindToParent ((LPCITEMIDLIST) pidl, IID_IShellFolder, (void **) &m_psfFolder, (LPCITEMIDLIST *) &pidlItem); joachim99@69: joachim99@69: m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); // allocate ony for one elemnt joachim99@69: m_pidlArray[0] = CopyPIDL (pidlItem); joachim99@69: joachim99@69: joachim99@69: // now free pidlItem via IMalloc interface (but not m_psfFolder, that we need later joachim99@69: LPMALLOC lpMalloc = NULL; joachim99@69: SHGetMalloc (&lpMalloc); joachim99@69: lpMalloc->Free (pidlItem); joachim99@69: lpMalloc->Release(); joachim99@69: joachim99@69: nItems = 1; joachim99@69: bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu joachim99@69: */ joachim99@69: } joachim99@69: joachim99@69: joachim99@69: // IShellFolder interface with a relative pidl has been passed joachim99@69: void CShellContextMenu::SetObjects(IShellFolder *psfFolder, LPITEMIDLIST pidlItem) joachim99@69: { joachim99@69: // free all allocated datas joachim99@69: if (m_psfFolder && bDelete) joachim99@69: m_psfFolder->Release (); joachim99@69: m_psfFolder = NULL; joachim99@69: FreePIDLArray (m_pidlArray); joachim99@69: m_pidlArray = NULL; joachim99@69: joachim99@69: m_psfFolder = psfFolder; joachim99@69: joachim99@69: m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); joachim99@69: m_pidlArray[0] = CopyPIDL (pidlItem); joachim99@69: joachim99@69: nItems = 1; joachim99@69: bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu joachim99@69: } joachim99@69: joachim99@69: void CShellContextMenu::SetObjects(IShellFolder * psfFolder, LPITEMIDLIST *pidlArray, int nItemCount) joachim99@69: { joachim99@69: // free all allocated datas joachim99@69: if (m_psfFolder && bDelete) joachim99@69: m_psfFolder->Release (); joachim99@69: m_psfFolder = NULL; joachim99@69: FreePIDLArray (m_pidlArray); joachim99@69: m_pidlArray = NULL; joachim99@69: joachim99@69: m_psfFolder = psfFolder; joachim99@69: joachim99@69: m_pidlArray = (LPITEMIDLIST *) malloc (nItemCount * sizeof (LPITEMIDLIST)); joachim99@69: joachim99@69: for (int i = 0; i < nItemCount; i++) joachim99@69: m_pidlArray[i] = CopyPIDL (pidlArray[i]); joachim99@69: joachim99@69: nItems = nItemCount; joachim99@69: bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu joachim99@69: } joachim99@69: joachim99@69: joachim99@69: void CShellContextMenu::FreePIDLArray(LPITEMIDLIST *pidlArray) joachim99@69: { joachim99@69: if (!pidlArray) joachim99@69: return; joachim99@69: joachim99@69: int iSize = _msize (pidlArray) / sizeof (LPITEMIDLIST); joachim99@69: joachim99@69: for (int i = 0; i < iSize; i++) joachim99@69: free (pidlArray[i]); joachim99@69: free (pidlArray); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: LPITEMIDLIST CShellContextMenu::CopyPIDL (LPCITEMIDLIST pidl, int cb) joachim99@69: { joachim99@69: if (cb == -1) joachim99@69: cb = GetPIDLSize (pidl); // Calculate size of list. joachim99@69: joachim99@69: LPITEMIDLIST pidlRet = (LPITEMIDLIST) calloc (cb + sizeof (USHORT), sizeof (BYTE)); joachim99@69: if (pidlRet) joachim99@69: CopyMemory(pidlRet, pidl, cb); joachim99@69: joachim99@69: return (pidlRet); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: UINT CShellContextMenu::GetPIDLSize (LPCITEMIDLIST pidl) joachim99@69: { joachim99@69: if (!pidl) joachim99@69: return 0; joachim99@69: int nSize = 0; joachim99@69: LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl; joachim99@69: while (pidlTemp->mkid.cb) joachim99@69: { joachim99@69: nSize += pidlTemp->mkid.cb; joachim99@69: pidlTemp = (LPITEMIDLIST) (((LPBYTE) pidlTemp) + pidlTemp->mkid.cb); joachim99@69: } joachim99@69: return nSize; joachim99@69: } joachim99@69: joachim99@69: HMENU CShellContextMenu::GetMenu() joachim99@69: { joachim99@69: if (!m_hMenu) joachim99@69: { joachim99@69: m_hMenu = CreatePopupMenu(); // create the popupmenu (its empty) joachim99@69: } joachim99@69: return (m_hMenu); joachim99@69: } joachim99@69: joachim99@69: joachim99@69: // this is workaround function for the Shell API Function SHBindToParent joachim99@69: // SHBindToParent is not available under Win95/98 joachim99@69: HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast) joachim99@69: { joachim99@69: HRESULT hr = 0; joachim99@69: if (!pidl || !ppv) joachim99@69: return E_POINTER; joachim99@69: joachim99@69: int nCount = GetPIDLCount (pidl); joachim99@69: if (nCount == 0) // desktop pidl of invalid pidl joachim99@69: return E_POINTER; joachim99@69: joachim99@69: IShellFolder * psfDesktop = NULL; joachim99@69: SHGetDesktopFolder (&psfDesktop); joachim99@69: if (nCount == 1) // desktop pidl joachim99@69: { joachim99@69: if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK) joachim99@69: { joachim99@69: if (ppidlLast) joachim99@69: *ppidlLast = CopyPIDL (pidl); joachim99@69: } joachim99@69: psfDesktop->Release (); joachim99@69: return hr; joachim99@69: } joachim99@69: joachim99@69: LPBYTE pRel = GetPIDLPos (pidl, nCount - 1); joachim99@69: LPITEMIDLIST pidlParent = NULL; joachim99@69: pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl); joachim99@69: IShellFolder * psfFolder = NULL; joachim99@69: joachim99@69: if ((hr = psfDesktop->BindToObject (pidlParent, NULL, IID_IShellFolder, (void **) &psfFolder)) != S_OK) joachim99@69: { joachim99@69: free (pidlParent); joachim99@69: psfDesktop->Release (); joachim99@69: return hr; joachim99@69: } joachim99@69: if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK) joachim99@69: { joachim99@69: if (ppidlLast) joachim99@69: *ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel); joachim99@69: } joachim99@69: free (pidlParent); joachim99@69: psfFolder->Release (); joachim99@69: psfDesktop->Release (); joachim99@69: return hr; joachim99@69: } joachim99@69: joachim99@69: joachim99@69: LPBYTE CShellContextMenu::GetPIDLPos (LPCITEMIDLIST pidl, int nPos) joachim99@69: { joachim99@69: if (!pidl) joachim99@69: return 0; joachim99@69: int nCount = 0; joachim99@69: joachim99@69: BYTE * pCur = (BYTE *) pidl; joachim99@69: while (((LPCITEMIDLIST) pCur)->mkid.cb) joachim99@69: { joachim99@69: if (nCount == nPos) joachim99@69: return pCur; joachim99@69: nCount++; joachim99@69: pCur += ((LPCITEMIDLIST) pCur)->mkid.cb; // + sizeof(pidl->mkid.cb); joachim99@69: } joachim99@69: if (nCount == nPos) joachim99@69: return pCur; joachim99@69: return NULL; joachim99@69: } joachim99@69: joachim99@69: joachim99@69: int CShellContextMenu::GetPIDLCount (LPCITEMIDLIST pidl) joachim99@69: { joachim99@69: if (!pidl) joachim99@69: return 0; joachim99@69: joachim99@69: int nCount = 0; joachim99@69: BYTE* pCur = (BYTE *) pidl; joachim99@69: while (((LPCITEMIDLIST) pCur)->mkid.cb) joachim99@69: { joachim99@69: nCount++; joachim99@69: pCur += ((LPCITEMIDLIST) pCur)->mkid.cb; joachim99@69: } joachim99@69: return nCount; joachim99@69: } joachim99@69: joachim99@69: #endif joachim99@69: