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