comparison kdiff3/src-QT4/kreplacements/ShellContextMenu.cpp @ 75:08ea9b86c12c

KDiff3-0.9.91
author joachim99
date Sat, 04 Nov 2006 00:05:00 +0000
parents kdiff3/src/kreplacements/ShellContextMenu.cpp@5bbfe4784324
children 1184fc843210
comparison
equal deleted inserted replaced
74:069521efec1a 75:08ea9b86c12c
1 // ShellContextMenu.cpp: Implementierung der Klasse CShellContextMenu.
2 //
3 //////////////////////////////////////////////////////////////////////
4 #ifdef _WIN32
5 #include <windows.h>
6 #include <shlobj.h>
7 #include <malloc.h>
8 #include <qstring.h>
9 #include <qstringlist.h>
10 #include <qwidget.h>
11 #include <qdir.h>
12 #include <QMenu>
13 #include "ShellContextMenu.h"
14
15 #ifdef _DEBUG
16 #undef THIS_FILE
17 static char THIS_FILE[]=__FILE__;
18 #define new DEBUG_NEW
19 #endif
20
21 //////////////////////////////////////////////////////////////////////
22 // Konstruktion/Destruktion
23 //////////////////////////////////////////////////////////////////////
24
25 #define MIN_ID 100
26 #define MAX_ID 10000
27
28
29 void showShellContextMenu( const QString& itemPath, QPoint pt, QWidget* pParentWidget, QMenu* pMenu )
30 {
31 CShellContextMenu scm;
32 scm.SetObjects(QDir::convertSeparators(itemPath));
33 int id = scm.ShowContextMenu (pParentWidget, pt, pMenu);
34 if (id>=1)
35 pMenu->actions().value(id-1)->trigger();
36 }
37
38 IContextMenu2 * g_IContext2 = NULL;
39 IContextMenu3 * g_IContext3 = NULL;
40
41 CShellContextMenu::CShellContextMenu()
42 {
43 m_psfFolder = NULL;
44 m_pidlArray = NULL;
45 m_hMenu = NULL;
46 }
47
48 CShellContextMenu::~CShellContextMenu()
49 {
50 // free all allocated datas
51 if (m_psfFolder && bDelete)
52 m_psfFolder->Release ();
53 m_psfFolder = NULL;
54 FreePIDLArray (m_pidlArray);
55 m_pidlArray = NULL;
56
57 if (m_hMenu)
58 DestroyMenu( m_hMenu );
59 }
60
61
62
63 // this functions determines which version of IContextMenu is avaibale for those objects (always the highest one)
64 // and returns that interface
65 BOOL CShellContextMenu::GetContextMenu (void ** ppContextMenu, int & iMenuType)
66 {
67 *ppContextMenu = NULL;
68 LPCONTEXTMENU icm1 = NULL;
69
70 // first we retrieve the normal IContextMenu interface (every object should have it)
71 m_psfFolder->GetUIObjectOf (NULL, nItems, (LPCITEMIDLIST *) m_pidlArray, IID_IContextMenu, NULL, (void**) &icm1);
72
73 if (icm1)
74 { // since we got an IContextMenu interface we can now obtain the higher version interfaces via that
75 if (icm1->QueryInterface (IID_IContextMenu3, ppContextMenu) == NOERROR)
76 iMenuType = 3;
77 else if (icm1->QueryInterface (IID_IContextMenu2, ppContextMenu) == NOERROR)
78 iMenuType = 2;
79
80 if (*ppContextMenu)
81 icm1->Release(); // we can now release version 1 interface, cause we got a higher one
82 else
83 {
84 iMenuType = 1;
85 *ppContextMenu = icm1; // since no higher versions were found
86 } // redirect ppContextMenu to version 1 interface
87 }
88 else
89 return (FALSE); // something went wrong
90
91 return (TRUE); // success
92 }
93
94
95 LRESULT CALLBACK CShellContextMenu::HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
96 {
97 switch (message)
98 {
99 case WM_MENUCHAR: // only supported by IContextMenu3
100 if (g_IContext3)
101 {
102 LRESULT lResult = 0;
103 g_IContext3->HandleMenuMsg2 (message, wParam, lParam, &lResult);
104 return (lResult);
105 }
106 break;
107
108 case WM_DRAWITEM:
109 case WM_MEASUREITEM:
110 if (wParam)
111 break; // if wParam != 0 then the message is not menu-related
112
113 case WM_INITMENUPOPUP:
114 if (g_IContext2)
115 g_IContext2->HandleMenuMsg (message, wParam, lParam);
116 else // version 3
117 g_IContext3->HandleMenuMsg (message, wParam, lParam);
118 return (message == WM_INITMENUPOPUP ? 0 : TRUE); // inform caller that we handled WM_INITPOPUPMENU by ourself
119 break;
120
121 default:
122 break;
123 }
124
125 // call original WndProc of window to prevent undefined bevhaviour of window
126 return ::CallWindowProc ((WNDPROC) GetProp ( hWnd, TEXT ("OldWndProc")), hWnd, message, wParam, lParam);
127 }
128
129
130 UINT CShellContextMenu::ShowContextMenu(QWidget * pParentWidget, QPoint pt, QMenu* pMenu )
131 {
132 HWND hWnd = pParentWidget->winId();
133 int iMenuType = 0; // to know which version of IContextMenu is supported
134 LPCONTEXTMENU pContextMenu; // common pointer to IContextMenu and higher version interface
135
136 if (!GetContextMenu ((void**) &pContextMenu, iMenuType))
137 return (0); // something went wrong
138
139 if (!m_hMenu)
140 {
141 DestroyMenu( m_hMenu );
142 m_hMenu = CreatePopupMenu ();
143 }
144
145 int i;
146 QList<QAction*> actionList = pMenu->actions();
147 for( i=0; i<actionList.count(); ++i )
148 {
149 QString s = actionList.at(i)->text();
150 if (!s.isEmpty())
151 AppendMenuW( m_hMenu, MF_STRING, i+1, (LPCWSTR)s.utf16() );
152 }
153 AppendMenuW( m_hMenu, MF_SEPARATOR, i+1, L"" );
154
155 // lets fill the our popupmenu
156 pContextMenu->QueryContextMenu (m_hMenu, GetMenuItemCount (m_hMenu), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE);
157
158 // subclass window to handle menurelated messages in CShellContextMenu
159 WNDPROC OldWndProc;
160 if (iMenuType > 1) // only subclass if its version 2 or 3
161 {
162 OldWndProc = (WNDPROC) SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) HookWndProc);
163 if (iMenuType == 2)
164 g_IContext2 = (LPCONTEXTMENU2) pContextMenu;
165 else // version 3
166 g_IContext3 = (LPCONTEXTMENU3) pContextMenu;
167 }
168 else
169 OldWndProc = NULL;
170
171 UINT idCommand = TrackPopupMenu (m_hMenu,TPM_RETURNCMD | TPM_LEFTALIGN, pt.x(), pt.y(), 0, pParentWidget->winId(), 0);
172
173 if (OldWndProc) // unsubclass
174 SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) OldWndProc);
175
176 if (idCommand >= MIN_ID && idCommand <= MAX_ID) // see if returned idCommand belongs to shell menu entries
177 {
178 InvokeCommand (pContextMenu, idCommand - MIN_ID); // execute related command
179 idCommand = 0;
180 }
181
182 pContextMenu->Release();
183 g_IContext2 = NULL;
184 g_IContext3 = NULL;
185
186 return (idCommand);
187 }
188
189
190 void CShellContextMenu::InvokeCommand (LPCONTEXTMENU pContextMenu, UINT idCommand)
191 {
192 CMINVOKECOMMANDINFO cmi = {0};
193 cmi.cbSize = sizeof (CMINVOKECOMMANDINFO);
194 cmi.lpVerb = (LPSTR) MAKEINTRESOURCE (idCommand);
195 cmi.nShow = SW_SHOWNORMAL;
196
197 pContextMenu->InvokeCommand (&cmi);
198 }
199
200
201 void CShellContextMenu::SetObjects(const QString& strObject)
202 {
203 // only one object is passed
204 QStringList strArray;
205 strArray << strObject; // create a CStringArray with one element
206
207 SetObjects (strArray); // and pass it to SetObjects (CStringArray &strArray)
208 // for further processing
209 }
210
211
212 void CShellContextMenu::SetObjects(const QStringList &strList)
213 {
214 // free all allocated datas
215 if (m_psfFolder && bDelete)
216 m_psfFolder->Release ();
217 m_psfFolder = NULL;
218 FreePIDLArray (m_pidlArray);
219 m_pidlArray = NULL;
220
221 // get IShellFolder interface of Desktop (root of shell namespace)
222 IShellFolder * psfDesktop = NULL;
223 SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl
224
225 // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface
226 // but since we use the Desktop as our interface and the Desktop is the namespace root
227 // that means that it's a fully qualified PIDL, which is what we need
228 LPITEMIDLIST pidl = NULL;
229
230 psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[0].utf16(), NULL, &pidl, NULL);
231
232 // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface
233 LPITEMIDLIST pidlItem = NULL; // relative pidl
234 SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL);
235 free (pidlItem);
236 // get interface to IMalloc (need to free the PIDLs allocated by the shell functions)
237 LPMALLOC lpMalloc = NULL;
238 SHGetMalloc (&lpMalloc);
239 lpMalloc->Free (pidl);
240
241 // now we have the IShellFolder interface to the parent folder specified in the first element in strArray
242 // since we assume that all objects are in the same folder (as it's stated in the MSDN)
243 // we now have the IShellFolder interface to every objects parent folder
244
245 IShellFolder * psfFolder = NULL;
246 nItems = strList.size ();
247 for (int i = 0; i < nItems; i++)
248 {
249 pidl=0;
250 psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[i].utf16(), NULL, &pidl, NULL);
251 if (pidl)
252 {
253 m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST));
254 // get relative pidl via SHBindToParent
255 SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem);
256 m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray
257 free (pidlItem);
258 lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName
259 psfFolder->Release ();
260 }
261 }
262 lpMalloc->Release ();
263 psfDesktop->Release ();
264
265 bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu
266 }
267
268
269 // only one full qualified PIDL has been passed
270 void CShellContextMenu::SetObjects(LPITEMIDLIST /*pidl*/)
271 {
272 /*
273 // free all allocated datas
274 if (m_psfFolder && bDelete)
275 m_psfFolder->Release ();
276 m_psfFolder = NULL;
277 FreePIDLArray (m_pidlArray);
278 m_pidlArray = NULL;
279
280 // full qualified PIDL is passed so we need
281 // its parent IShellFolder interface and its relative PIDL to that
282 LPITEMIDLIST pidlItem = NULL;
283 SHBindToParent ((LPCITEMIDLIST) pidl, IID_IShellFolder, (void **) &m_psfFolder, (LPCITEMIDLIST *) &pidlItem);
284
285 m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); // allocate ony for one elemnt
286 m_pidlArray[0] = CopyPIDL (pidlItem);
287
288
289 // now free pidlItem via IMalloc interface (but not m_psfFolder, that we need later
290 LPMALLOC lpMalloc = NULL;
291 SHGetMalloc (&lpMalloc);
292 lpMalloc->Free (pidlItem);
293 lpMalloc->Release();
294
295 nItems = 1;
296 bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu
297 */
298 }
299
300
301 // IShellFolder interface with a relative pidl has been passed
302 void CShellContextMenu::SetObjects(IShellFolder *psfFolder, LPITEMIDLIST pidlItem)
303 {
304 // free all allocated datas
305 if (m_psfFolder && bDelete)
306 m_psfFolder->Release ();
307 m_psfFolder = NULL;
308 FreePIDLArray (m_pidlArray);
309 m_pidlArray = NULL;
310
311 m_psfFolder = psfFolder;
312
313 m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST));
314 m_pidlArray[0] = CopyPIDL (pidlItem);
315
316 nItems = 1;
317 bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu
318 }
319
320 void CShellContextMenu::SetObjects(IShellFolder * psfFolder, LPITEMIDLIST *pidlArray, int nItemCount)
321 {
322 // free all allocated datas
323 if (m_psfFolder && bDelete)
324 m_psfFolder->Release ();
325 m_psfFolder = NULL;
326 FreePIDLArray (m_pidlArray);
327 m_pidlArray = NULL;
328
329 m_psfFolder = psfFolder;
330
331 m_pidlArray = (LPITEMIDLIST *) malloc (nItemCount * sizeof (LPITEMIDLIST));
332
333 for (int i = 0; i < nItemCount; i++)
334 m_pidlArray[i] = CopyPIDL (pidlArray[i]);
335
336 nItems = nItemCount;
337 bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu
338 }
339
340
341 void CShellContextMenu::FreePIDLArray(LPITEMIDLIST *pidlArray)
342 {
343 if (!pidlArray)
344 return;
345
346 int iSize = _msize (pidlArray) / sizeof (LPITEMIDLIST);
347
348 for (int i = 0; i < iSize; i++)
349 free (pidlArray[i]);
350 free (pidlArray);
351 }
352
353
354 LPITEMIDLIST CShellContextMenu::CopyPIDL (LPCITEMIDLIST pidl, int cb)
355 {
356 if (cb == -1)
357 cb = GetPIDLSize (pidl); // Calculate size of list.
358
359 LPITEMIDLIST pidlRet = (LPITEMIDLIST) calloc (cb + sizeof (USHORT), sizeof (BYTE));
360 if (pidlRet)
361 CopyMemory(pidlRet, pidl, cb);
362
363 return (pidlRet);
364 }
365
366
367 UINT CShellContextMenu::GetPIDLSize (LPCITEMIDLIST pidl)
368 {
369 if (!pidl)
370 return 0;
371 int nSize = 0;
372 LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl;
373 while (pidlTemp->mkid.cb)
374 {
375 nSize += pidlTemp->mkid.cb;
376 pidlTemp = (LPITEMIDLIST) (((LPBYTE) pidlTemp) + pidlTemp->mkid.cb);
377 }
378 return nSize;
379 }
380
381 HMENU CShellContextMenu::GetMenu()
382 {
383 if (!m_hMenu)
384 {
385 m_hMenu = CreatePopupMenu(); // create the popupmenu (its empty)
386 }
387 return (m_hMenu);
388 }
389
390
391 // this is workaround function for the Shell API Function SHBindToParent
392 // SHBindToParent is not available under Win95/98
393 HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast)
394 {
395 HRESULT hr = 0;
396 if (!pidl || !ppv)
397 return E_POINTER;
398
399 int nCount = GetPIDLCount (pidl);
400 if (nCount == 0) // desktop pidl of invalid pidl
401 return E_POINTER;
402
403 IShellFolder * psfDesktop = NULL;
404 SHGetDesktopFolder (&psfDesktop);
405 if (nCount == 1) // desktop pidl
406 {
407 if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK)
408 {
409 if (ppidlLast)
410 *ppidlLast = CopyPIDL (pidl);
411 }
412 psfDesktop->Release ();
413 return hr;
414 }
415
416 LPBYTE pRel = GetPIDLPos (pidl, nCount - 1);
417 LPITEMIDLIST pidlParent = NULL;
418 pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl);
419 IShellFolder * psfFolder = NULL;
420
421 if ((hr = psfDesktop->BindToObject (pidlParent, NULL, IID_IShellFolder, (void **) &psfFolder)) != S_OK)
422 {
423 free (pidlParent);
424 psfDesktop->Release ();
425 return hr;
426 }
427 if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK)
428 {
429 if (ppidlLast)
430 *ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel);
431 }
432 free (pidlParent);
433 psfFolder->Release ();
434 psfDesktop->Release ();
435 return hr;
436 }
437
438
439 LPBYTE CShellContextMenu::GetPIDLPos (LPCITEMIDLIST pidl, int nPos)
440 {
441 if (!pidl)
442 return 0;
443 int nCount = 0;
444
445 BYTE * pCur = (BYTE *) pidl;
446 while (((LPCITEMIDLIST) pCur)->mkid.cb)
447 {
448 if (nCount == nPos)
449 return pCur;
450 nCount++;
451 pCur += ((LPCITEMIDLIST) pCur)->mkid.cb; // + sizeof(pidl->mkid.cb);
452 }
453 if (nCount == nPos)
454 return pCur;
455 return NULL;
456 }
457
458
459 int CShellContextMenu::GetPIDLCount (LPCITEMIDLIST pidl)
460 {
461 if (!pidl)
462 return 0;
463
464 int nCount = 0;
465 BYTE* pCur = (BYTE *) pidl;
466 while (((LPCITEMIDLIST) pCur)->mkid.cb)
467 {
468 nCount++;
469 pCur += ((LPCITEMIDLIST) pCur)->mkid.cb;
470 }
471 return nCount;
472 }
473
474 #endif
475