Mercurial > hg > easyhg-kdiff3
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 |