Mercurial > hg > easyhg-kdiff3
comparison kdiff3/src/kreplacements/ShellContextMenu.cpp @ 69:8febbfb1148c
KDiff3 0.9.89
author | joachim99 |
---|---|
date | Mon, 10 Apr 2006 08:40:51 +0000 |
parents | |
children | 5bbfe4784324 1184fc843210 |
comparison
equal
deleted
inserted
replaced
68:d7cafcda8c99 | 69:8febbfb1148c |
---|---|
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 <qpopupmenu.h> | |
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, QPopupMenu* 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->activateItemAt(id-1); | |
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, QPopupMenu* 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 UINT_PTR i; | |
146 for( i=0; i<pMenu->count(); ++i ) | |
147 { | |
148 QString s = pMenu->text(pMenu->idAt(i)); | |
149 if (!s.isEmpty()) | |
150 AppendMenuW( m_hMenu, MF_STRING, i+1, (LPCWSTR)s.ucs2() ); | |
151 } | |
152 AppendMenuW( m_hMenu, MF_SEPARATOR, i+1, L"" ); | |
153 | |
154 // lets fill the our popupmenu | |
155 pContextMenu->QueryContextMenu (m_hMenu, GetMenuItemCount (m_hMenu), MIN_ID, MAX_ID, CMF_NORMAL | CMF_EXPLORE); | |
156 | |
157 // subclass window to handle menurelated messages in CShellContextMenu | |
158 WNDPROC OldWndProc; | |
159 if (iMenuType > 1) // only subclass if its version 2 or 3 | |
160 { | |
161 OldWndProc = (WNDPROC) SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) HookWndProc); | |
162 if (iMenuType == 2) | |
163 g_IContext2 = (LPCONTEXTMENU2) pContextMenu; | |
164 else // version 3 | |
165 g_IContext3 = (LPCONTEXTMENU3) pContextMenu; | |
166 } | |
167 else | |
168 OldWndProc = NULL; | |
169 | |
170 UINT idCommand = TrackPopupMenu (m_hMenu,TPM_RETURNCMD | TPM_LEFTALIGN, pt.x(), pt.y(), 0, pParentWidget->winId(), 0); | |
171 | |
172 if (OldWndProc) // unsubclass | |
173 SetWindowLong (hWnd, GWL_WNDPROC, (DWORD) OldWndProc); | |
174 | |
175 if (idCommand >= MIN_ID && idCommand <= MAX_ID) // see if returned idCommand belongs to shell menu entries | |
176 { | |
177 InvokeCommand (pContextMenu, idCommand - MIN_ID); // execute related command | |
178 idCommand = 0; | |
179 } | |
180 | |
181 pContextMenu->Release(); | |
182 g_IContext2 = NULL; | |
183 g_IContext3 = NULL; | |
184 | |
185 return (idCommand); | |
186 } | |
187 | |
188 | |
189 void CShellContextMenu::InvokeCommand (LPCONTEXTMENU pContextMenu, UINT idCommand) | |
190 { | |
191 CMINVOKECOMMANDINFO cmi = {0}; | |
192 cmi.cbSize = sizeof (CMINVOKECOMMANDINFO); | |
193 cmi.lpVerb = (LPSTR) MAKEINTRESOURCE (idCommand); | |
194 cmi.nShow = SW_SHOWNORMAL; | |
195 | |
196 pContextMenu->InvokeCommand (&cmi); | |
197 } | |
198 | |
199 | |
200 void CShellContextMenu::SetObjects(const QString& strObject) | |
201 { | |
202 // only one object is passed | |
203 QStringList strArray; | |
204 strArray << strObject; // create a CStringArray with one element | |
205 | |
206 SetObjects (strArray); // and pass it to SetObjects (CStringArray &strArray) | |
207 // for further processing | |
208 } | |
209 | |
210 | |
211 void CShellContextMenu::SetObjects(const QStringList &strList) | |
212 { | |
213 // free all allocated datas | |
214 if (m_psfFolder && bDelete) | |
215 m_psfFolder->Release (); | |
216 m_psfFolder = NULL; | |
217 FreePIDLArray (m_pidlArray); | |
218 m_pidlArray = NULL; | |
219 | |
220 // get IShellFolder interface of Desktop (root of shell namespace) | |
221 IShellFolder * psfDesktop = NULL; | |
222 SHGetDesktopFolder (&psfDesktop); // needed to obtain full qualified pidl | |
223 | |
224 // ParseDisplayName creates a PIDL from a file system path relative to the IShellFolder interface | |
225 // but since we use the Desktop as our interface and the Desktop is the namespace root | |
226 // that means that it's a fully qualified PIDL, which is what we need | |
227 LPITEMIDLIST pidl = NULL; | |
228 | |
229 psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[0].ucs2(), NULL, &pidl, NULL); | |
230 | |
231 // now we need the parent IShellFolder interface of pidl, and the relative PIDL to that interface | |
232 LPITEMIDLIST pidlItem = NULL; // relative pidl | |
233 SHBindToParentEx (pidl, IID_IShellFolder, (void **) &m_psfFolder, NULL); | |
234 free (pidlItem); | |
235 // get interface to IMalloc (need to free the PIDLs allocated by the shell functions) | |
236 LPMALLOC lpMalloc = NULL; | |
237 SHGetMalloc (&lpMalloc); | |
238 lpMalloc->Free (pidl); | |
239 | |
240 // now we have the IShellFolder interface to the parent folder specified in the first element in strArray | |
241 // since we assume that all objects are in the same folder (as it's stated in the MSDN) | |
242 // we now have the IShellFolder interface to every objects parent folder | |
243 | |
244 IShellFolder * psfFolder = NULL; | |
245 nItems = strList.size (); | |
246 for (int i = 0; i < nItems; i++) | |
247 { | |
248 pidl=0; | |
249 psfDesktop->ParseDisplayName (NULL, 0, (LPOLESTR)strList[i].ucs2(), NULL, &pidl, NULL); | |
250 if (pidl) | |
251 { | |
252 m_pidlArray = (LPITEMIDLIST *) realloc (m_pidlArray, (i + 1) * sizeof (LPITEMIDLIST)); | |
253 // get relative pidl via SHBindToParent | |
254 SHBindToParentEx (pidl, IID_IShellFolder, (void **) &psfFolder, (LPCITEMIDLIST *) &pidlItem); | |
255 m_pidlArray[i] = CopyPIDL (pidlItem); // copy relative pidl to pidlArray | |
256 free (pidlItem); | |
257 lpMalloc->Free (pidl); // free pidl allocated by ParseDisplayName | |
258 psfFolder->Release (); | |
259 } | |
260 } | |
261 lpMalloc->Release (); | |
262 psfDesktop->Release (); | |
263 | |
264 bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu | |
265 } | |
266 | |
267 | |
268 // only one full qualified PIDL has been passed | |
269 void CShellContextMenu::SetObjects(LPITEMIDLIST /*pidl*/) | |
270 { | |
271 /* | |
272 // free all allocated datas | |
273 if (m_psfFolder && bDelete) | |
274 m_psfFolder->Release (); | |
275 m_psfFolder = NULL; | |
276 FreePIDLArray (m_pidlArray); | |
277 m_pidlArray = NULL; | |
278 | |
279 // full qualified PIDL is passed so we need | |
280 // its parent IShellFolder interface and its relative PIDL to that | |
281 LPITEMIDLIST pidlItem = NULL; | |
282 SHBindToParent ((LPCITEMIDLIST) pidl, IID_IShellFolder, (void **) &m_psfFolder, (LPCITEMIDLIST *) &pidlItem); | |
283 | |
284 m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); // allocate ony for one elemnt | |
285 m_pidlArray[0] = CopyPIDL (pidlItem); | |
286 | |
287 | |
288 // now free pidlItem via IMalloc interface (but not m_psfFolder, that we need later | |
289 LPMALLOC lpMalloc = NULL; | |
290 SHGetMalloc (&lpMalloc); | |
291 lpMalloc->Free (pidlItem); | |
292 lpMalloc->Release(); | |
293 | |
294 nItems = 1; | |
295 bDelete = TRUE; // indicates that m_psfFolder should be deleted by CShellContextMenu | |
296 */ | |
297 } | |
298 | |
299 | |
300 // IShellFolder interface with a relative pidl has been passed | |
301 void CShellContextMenu::SetObjects(IShellFolder *psfFolder, LPITEMIDLIST pidlItem) | |
302 { | |
303 // free all allocated datas | |
304 if (m_psfFolder && bDelete) | |
305 m_psfFolder->Release (); | |
306 m_psfFolder = NULL; | |
307 FreePIDLArray (m_pidlArray); | |
308 m_pidlArray = NULL; | |
309 | |
310 m_psfFolder = psfFolder; | |
311 | |
312 m_pidlArray = (LPITEMIDLIST *) malloc (sizeof (LPITEMIDLIST)); | |
313 m_pidlArray[0] = CopyPIDL (pidlItem); | |
314 | |
315 nItems = 1; | |
316 bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu | |
317 } | |
318 | |
319 void CShellContextMenu::SetObjects(IShellFolder * psfFolder, LPITEMIDLIST *pidlArray, int nItemCount) | |
320 { | |
321 // free all allocated datas | |
322 if (m_psfFolder && bDelete) | |
323 m_psfFolder->Release (); | |
324 m_psfFolder = NULL; | |
325 FreePIDLArray (m_pidlArray); | |
326 m_pidlArray = NULL; | |
327 | |
328 m_psfFolder = psfFolder; | |
329 | |
330 m_pidlArray = (LPITEMIDLIST *) malloc (nItemCount * sizeof (LPITEMIDLIST)); | |
331 | |
332 for (int i = 0; i < nItemCount; i++) | |
333 m_pidlArray[i] = CopyPIDL (pidlArray[i]); | |
334 | |
335 nItems = nItemCount; | |
336 bDelete = FALSE; // indicates wheter m_psfFolder should be deleted by CShellContextMenu | |
337 } | |
338 | |
339 | |
340 void CShellContextMenu::FreePIDLArray(LPITEMIDLIST *pidlArray) | |
341 { | |
342 if (!pidlArray) | |
343 return; | |
344 | |
345 int iSize = _msize (pidlArray) / sizeof (LPITEMIDLIST); | |
346 | |
347 for (int i = 0; i < iSize; i++) | |
348 free (pidlArray[i]); | |
349 free (pidlArray); | |
350 } | |
351 | |
352 | |
353 LPITEMIDLIST CShellContextMenu::CopyPIDL (LPCITEMIDLIST pidl, int cb) | |
354 { | |
355 if (cb == -1) | |
356 cb = GetPIDLSize (pidl); // Calculate size of list. | |
357 | |
358 LPITEMIDLIST pidlRet = (LPITEMIDLIST) calloc (cb + sizeof (USHORT), sizeof (BYTE)); | |
359 if (pidlRet) | |
360 CopyMemory(pidlRet, pidl, cb); | |
361 | |
362 return (pidlRet); | |
363 } | |
364 | |
365 | |
366 UINT CShellContextMenu::GetPIDLSize (LPCITEMIDLIST pidl) | |
367 { | |
368 if (!pidl) | |
369 return 0; | |
370 int nSize = 0; | |
371 LPITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl; | |
372 while (pidlTemp->mkid.cb) | |
373 { | |
374 nSize += pidlTemp->mkid.cb; | |
375 pidlTemp = (LPITEMIDLIST) (((LPBYTE) pidlTemp) + pidlTemp->mkid.cb); | |
376 } | |
377 return nSize; | |
378 } | |
379 | |
380 HMENU CShellContextMenu::GetMenu() | |
381 { | |
382 if (!m_hMenu) | |
383 { | |
384 m_hMenu = CreatePopupMenu(); // create the popupmenu (its empty) | |
385 } | |
386 return (m_hMenu); | |
387 } | |
388 | |
389 | |
390 // this is workaround function for the Shell API Function SHBindToParent | |
391 // SHBindToParent is not available under Win95/98 | |
392 HRESULT CShellContextMenu::SHBindToParentEx (LPCITEMIDLIST pidl, REFIID riid, VOID **ppv, LPCITEMIDLIST *ppidlLast) | |
393 { | |
394 HRESULT hr = 0; | |
395 if (!pidl || !ppv) | |
396 return E_POINTER; | |
397 | |
398 int nCount = GetPIDLCount (pidl); | |
399 if (nCount == 0) // desktop pidl of invalid pidl | |
400 return E_POINTER; | |
401 | |
402 IShellFolder * psfDesktop = NULL; | |
403 SHGetDesktopFolder (&psfDesktop); | |
404 if (nCount == 1) // desktop pidl | |
405 { | |
406 if ((hr = psfDesktop->QueryInterface(riid, ppv)) == S_OK) | |
407 { | |
408 if (ppidlLast) | |
409 *ppidlLast = CopyPIDL (pidl); | |
410 } | |
411 psfDesktop->Release (); | |
412 return hr; | |
413 } | |
414 | |
415 LPBYTE pRel = GetPIDLPos (pidl, nCount - 1); | |
416 LPITEMIDLIST pidlParent = NULL; | |
417 pidlParent = CopyPIDL (pidl, pRel - (LPBYTE) pidl); | |
418 IShellFolder * psfFolder = NULL; | |
419 | |
420 if ((hr = psfDesktop->BindToObject (pidlParent, NULL, IID_IShellFolder, (void **) &psfFolder)) != S_OK) | |
421 { | |
422 free (pidlParent); | |
423 psfDesktop->Release (); | |
424 return hr; | |
425 } | |
426 if ((hr = psfFolder->QueryInterface (riid, ppv)) == S_OK) | |
427 { | |
428 if (ppidlLast) | |
429 *ppidlLast = CopyPIDL ((LPCITEMIDLIST) pRel); | |
430 } | |
431 free (pidlParent); | |
432 psfFolder->Release (); | |
433 psfDesktop->Release (); | |
434 return hr; | |
435 } | |
436 | |
437 | |
438 LPBYTE CShellContextMenu::GetPIDLPos (LPCITEMIDLIST pidl, int nPos) | |
439 { | |
440 if (!pidl) | |
441 return 0; | |
442 int nCount = 0; | |
443 | |
444 BYTE * pCur = (BYTE *) pidl; | |
445 while (((LPCITEMIDLIST) pCur)->mkid.cb) | |
446 { | |
447 if (nCount == nPos) | |
448 return pCur; | |
449 nCount++; | |
450 pCur += ((LPCITEMIDLIST) pCur)->mkid.cb; // + sizeof(pidl->mkid.cb); | |
451 } | |
452 if (nCount == nPos) | |
453 return pCur; | |
454 return NULL; | |
455 } | |
456 | |
457 | |
458 int CShellContextMenu::GetPIDLCount (LPCITEMIDLIST pidl) | |
459 { | |
460 if (!pidl) | |
461 return 0; | |
462 | |
463 int nCount = 0; | |
464 BYTE* pCur = (BYTE *) pidl; | |
465 while (((LPCITEMIDLIST) pCur)->mkid.cb) | |
466 { | |
467 nCount++; | |
468 pCur += ((LPCITEMIDLIST) pCur)->mkid.cb; | |
469 } | |
470 return nCount; | |
471 } | |
472 | |
473 #endif | |
474 |