851beb91ff349c370fec12f7e31cdabea02a909e
[reactos.git] / reactos / dll / win32 / shell32 / CDefaultContextMenu.cpp
1 /*
2 * PROJECT: shell32
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/shell32/shv_item_new.c
5 * PURPOSE: provides default context menu implementation
6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7 */
8
9 #include "precomp.h"
10
11 extern "C"
12 {
13 //fixme: this isn't in wine's shlwapi header, and the definition doesnt match the
14 // windows headers. When wine's header and lib are fixed this can be removed.
15 DWORD WINAPI SHAnsiToUnicode(LPCSTR lpSrcStr, LPWSTR lpDstStr, int iLen);
16 };
17
18 WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
19
20 typedef struct _DynamicShellEntry_
21 {
22 UINT iIdCmdFirst;
23 UINT NumIds;
24 CLSID ClassID;
25 IContextMenu *pCM;
26 struct _DynamicShellEntry_ *pNext;
27 } DynamicShellEntry, *PDynamicShellEntry;
28
29 typedef struct _StaticShellEntry_
30 {
31 LPWSTR szVerb;
32 HKEY hkClass;
33 struct _StaticShellEntry_ *pNext;
34 } StaticShellEntry, *PStaticShellEntry;
35
36
37 //
38 // verbs for InvokeCommandInfo
39 //
40 struct _StaticInvokeCommandMap_
41 {
42 LPCSTR szStringVerb;
43 UINT IntVerb;
44 } g_StaticInvokeCmdMap[] =
45 {
46 { "RunAs", 0 }, // Unimplemented
47 { "Print", 0 }, // Unimplemented
48 { "Preview", 0 }, // Unimplemented
49 { "Open", FCIDM_SHVIEW_OPEN },
50 { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER },
51 { CMDSTR_VIEWLISTA, FCIDM_SHVIEW_LISTVIEW },
52 { CMDSTR_VIEWDETAILSA, FCIDM_SHVIEW_REPORTVIEW }
53 };
54
55
56 class CDefaultContextMenu :
57 public CComObjectRootEx<CComMultiThreadModelNoCS>,
58 public IContextMenu3,
59 public IObjectWithSite
60 {
61 private:
62 CComPtr<IUnknown> m_site;
63 CComPtr<IShellFolder> m_psf;
64 CComPtr<IContextMenuCB> m_pmcb;
65 LPFNDFMCALLBACK m_pfnmcb;
66 UINT m_cidl;
67 PCUITEMID_CHILD_ARRAY m_apidl;
68 CComPtr<IDataObject> m_pDataObj;
69 HKEY* m_aKeys;
70 UINT m_cKeys;
71 PIDLIST_ABSOLUTE m_pidlFolder;
72 DWORD m_bGroupPolicyActive;
73 PDynamicShellEntry m_pDynamicEntries; /* first dynamic shell extension entry */
74 UINT m_iIdSHEFirst; /* first used id */
75 UINT m_iIdSHELast; /* last used id */
76 PStaticShellEntry m_pStaticEntries; /* first static shell extension entry */
77 UINT m_iIdSCMFirst; /* first static used id */
78 UINT m_iIdSCMLast; /* last static used id */
79 UINT m_iIdCBFirst; /* first callback used id */
80 UINT m_iIdCBLast; /* last callback used id */
81
82 HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam);
83 void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb);
84 void AddStaticEntriesForKey(HKEY hKey);
85 BOOL IsShellExtensionAlreadyLoaded(const CLSID *pclsid);
86 HRESULT LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid);
87 BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
88 UINT InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast);
89 UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast);
90 HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink);
91 HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi);
92 HRESULT DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi);
93 HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
94 HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy);
95 HRESULT DoRename(LPCMINVOKECOMMANDINFO lpcmi);
96 HRESULT DoProperties(LPCMINVOKECOMMANDINFO lpcmi);
97 HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFO lpici);
98 HRESULT DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
99 HRESULT DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
100 DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry);
101 HRESULT TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
102 HRESULT InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
103 PDynamicShellEntry GetDynamicEntry(UINT idCmd);
104 BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode);
105
106 public:
107 CDefaultContextMenu();
108 ~CDefaultContextMenu();
109 HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn);
110
111 // IContextMenu
112 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
113 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
114 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
115
116 // IContextMenu2
117 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
118
119 // IContextMenu3
120 virtual HRESULT WINAPI HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
121
122 // IObjectWithSite
123 virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite);
124 virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite);
125
126 BEGIN_COM_MAP(CDefaultContextMenu)
127 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
128 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
129 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3)
130 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
131 END_COM_MAP()
132 };
133
134 CDefaultContextMenu::CDefaultContextMenu() :
135 m_psf(NULL),
136 m_pmcb(NULL),
137 m_pfnmcb(NULL),
138 m_cidl(0),
139 m_apidl(NULL),
140 m_pDataObj(NULL),
141 m_aKeys(NULL),
142 m_cKeys(NULL),
143 m_pidlFolder(NULL),
144 m_bGroupPolicyActive(0),
145 m_pDynamicEntries(NULL),
146 m_iIdSHEFirst(0),
147 m_iIdSHELast(0),
148 m_pStaticEntries(NULL),
149 m_iIdSCMFirst(0),
150 m_iIdSCMLast(0),
151 m_iIdCBFirst(0),
152 m_iIdCBLast(0)
153 {
154 }
155
156 CDefaultContextMenu::~CDefaultContextMenu()
157 {
158 /* Free dynamic shell extension entries */
159 PDynamicShellEntry pDynamicEntry = m_pDynamicEntries, pNextDynamic;
160 while (pDynamicEntry)
161 {
162 pNextDynamic = pDynamicEntry->pNext;
163 pDynamicEntry->pCM->Release();
164 HeapFree(GetProcessHeap(), 0, pDynamicEntry);
165 pDynamicEntry = pNextDynamic;
166 }
167
168 /* Free static shell extension entries */
169 PStaticShellEntry pStaticEntry = m_pStaticEntries, pNextStatic;
170 while (pStaticEntry)
171 {
172 pNextStatic = pStaticEntry->pNext;
173 HeapFree(GetProcessHeap(), 0, pStaticEntry->szVerb);
174 HeapFree(GetProcessHeap(), 0, pStaticEntry);
175 pStaticEntry = pNextStatic;
176 }
177
178 for (UINT i = 0; i < m_cKeys; i++)
179 RegCloseKey(m_aKeys[i]);
180 HeapFree(GetProcessHeap(), 0, m_aKeys);
181
182 if (m_pidlFolder)
183 CoTaskMemFree(m_pidlFolder);
184 _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl);
185 }
186
187 HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn)
188 {
189 TRACE("cidl %u\n", pdcm->cidl);
190
191 if (!pdcm->pcmcb && !lpfn)
192 {
193 ERR("CDefaultContextMenu needs a callback!\n");
194 return E_INVALIDARG;
195 }
196
197 m_cidl = pdcm->cidl;
198 m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl));
199 if (m_cidl && !m_apidl)
200 return E_OUTOFMEMORY;
201 m_psf = pdcm->psf;
202 m_pmcb = pdcm->pcmcb;
203 m_pfnmcb = lpfn;
204
205 m_cKeys = pdcm->cKeys;
206 if (pdcm->cKeys)
207 {
208 m_aKeys = (HKEY*)HeapAlloc(GetProcessHeap(), 0, sizeof(HKEY) * pdcm->cKeys);
209 if (!m_aKeys)
210 return E_OUTOFMEMORY;
211 memcpy(m_aKeys, pdcm->aKeys, sizeof(HKEY) * pdcm->cKeys);
212 }
213
214 m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj));
215
216 if (pdcm->pidlFolder)
217 {
218 m_pidlFolder = ILClone(pdcm->pidlFolder);
219 }
220 else
221 {
222 CComPtr<IPersistFolder2> pf = NULL;
223 if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
224 {
225 if (FAILED(pf->GetCurFolder(reinterpret_cast<LPITEMIDLIST*>(&m_pidlFolder))))
226 ERR("GetCurFolder failed\n");
227 }
228 TRACE("pidlFolder %p\n", m_pidlFolder);
229 }
230
231 return S_OK;
232 }
233
234 HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam)
235 {
236 if (m_pmcb)
237 {
238 return m_pmcb->CallBack(m_psf, NULL, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
239 }
240 else if(m_pfnmcb)
241 {
242 return m_pfnmcb(m_psf, NULL, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
243 }
244
245 return E_FAIL;
246 }
247
248 void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb)
249 {
250 PStaticShellEntry pEntry = m_pStaticEntries, pLastEntry = NULL;
251 while(pEntry)
252 {
253 if (!wcsicmp(pEntry->szVerb, szVerb))
254 {
255 /* entry already exists */
256 return;
257 }
258 pLastEntry = pEntry;
259 pEntry = pEntry->pNext;
260 }
261
262 TRACE("adding verb %s\n", debugstr_w(szVerb));
263
264 pEntry = (StaticShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry));
265 if (pEntry)
266 {
267 pEntry->pNext = NULL;
268 pEntry->szVerb = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb) + 1) * sizeof(WCHAR));
269 if (pEntry->szVerb)
270 wcscpy(pEntry->szVerb, szVerb);
271 pEntry->hkClass = hkeyClass;
272 }
273
274 if (!wcsicmp(szVerb, L"open"))
275 {
276 /* open verb is always inserted in front */
277 pEntry->pNext = m_pStaticEntries;
278 m_pStaticEntries = pEntry;
279 }
280 else if (pLastEntry)
281 pLastEntry->pNext = pEntry;
282 else
283 m_pStaticEntries = pEntry;
284 }
285
286 void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey)
287 {
288 WCHAR wszName[40];
289 DWORD cchName, dwIndex = 0;
290 HKEY hShellKey;
291
292 LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey);
293 if (lres != STATUS_SUCCESS)
294 return;
295
296 while(TRUE)
297 {
298 cchName = _countof(wszName);
299 if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
300 break;
301
302 AddStaticEntry(hKey, wszName);
303 }
304
305 RegCloseKey(hShellKey);
306 }
307
308 static
309 BOOL
310 HasClipboardData()
311 {
312 BOOL bRet = FALSE;
313 CComPtr<IDataObject> pDataObj;
314
315 if (SUCCEEDED(OleGetClipboard(&pDataObj)))
316 {
317 STGMEDIUM medium;
318 FORMATETC formatetc;
319
320 TRACE("pDataObj=%p\n", pDataObj.p);
321
322 /* Set the FORMATETC structure*/
323 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
324 if (SUCCEEDED(pDataObj->GetData(&formatetc, &medium)))
325 {
326 bRet = TRUE;
327 ReleaseStgMedium(&medium);
328 }
329 }
330
331 return bRet;
332 }
333
334 BOOL
335 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid)
336 {
337 PDynamicShellEntry pEntry = m_pDynamicEntries;
338
339 while (pEntry)
340 {
341 if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID)))
342 return TRUE;
343 pEntry = pEntry->pNext;
344 }
345
346 return FALSE;
347 }
348
349 HRESULT
350 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid)
351 {
352 HRESULT hr;
353
354 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid));
355
356 if (IsShellExtensionAlreadyLoaded(pclsid))
357 return S_OK;
358
359 CComPtr<IContextMenu> pcm;
360 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
361 if (FAILED_UNEXPECTEDLY(hr))
362 return hr;
363
364 CComPtr<IShellExtInit> pExtInit;
365 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
366 if (FAILED_UNEXPECTEDLY(hr))
367 return hr;
368
369 hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
370 if (FAILED(hr))
371 {
372 WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr);
373 return hr;
374 }
375
376 PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
377 if (!pEntry)
378 return E_OUTOFMEMORY;
379
380 pEntry->iIdCmdFirst = 0;
381 pEntry->pNext = NULL;
382 pEntry->NumIds = 0;
383 pEntry->pCM = pcm.Detach();
384 memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID));
385
386 if (m_pDynamicEntries)
387 {
388 PDynamicShellEntry pLastEntry = m_pDynamicEntries;
389
390 while (pLastEntry->pNext)
391 pLastEntry = pLastEntry->pNext;
392
393 pLastEntry->pNext = pEntry;
394 }
395 else
396 m_pDynamicEntries = pEntry;
397
398 return S_OK;
399 }
400
401 BOOL
402 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
403 {
404 WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
405 DWORD cchName;
406 HRESULT hr;
407 HKEY hKey;
408
409 if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
410 {
411 TRACE("RegOpenKeyExW failed\n");
412 return FALSE;
413 }
414
415 DWORD dwIndex = 0;
416 while (TRUE)
417 {
418 cchName = _countof(wszName);
419 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
420 break;
421
422 /* Key name or key value is CLSID */
423 CLSID clsid;
424 hr = CLSIDFromString(wszName, &clsid);
425 if (hr == S_OK)
426 pwszClsid = wszName;
427 else
428 {
429 DWORD cchBuf = _countof(wszBuf);
430 if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
431 hr = CLSIDFromString(wszBuf, &clsid);
432 pwszClsid = wszBuf;
433 }
434
435 if (FAILED(hr))
436 {
437 ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr);
438 continue;
439 }
440
441 if (m_bGroupPolicyActive)
442 {
443 if (RegGetValueW(HKEY_LOCAL_MACHINE,
444 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
445 pwszClsid,
446 RRF_RT_REG_SZ,
447 NULL,
448 NULL,
449 NULL) != ERROR_SUCCESS)
450 {
451 ERR("Shell extension %s not approved!\n", pwszClsid);
452 continue;
453 }
454 }
455
456 hr = LoadDynamicContextMenuHandler(hKey, &clsid);
457 if (FAILED(hr))
458 WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid);
459 }
460
461 RegCloseKey(hKey);
462 return TRUE;
463 }
464
465 UINT
466 CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast)
467 {
468 if (!m_pDynamicEntries)
469 {
470 m_iIdSHEFirst = 0;
471 m_iIdSHELast = 0;
472 return IndexMenu;
473 }
474
475 PDynamicShellEntry pEntry = m_pDynamicEntries;
476 m_iIdSHEFirst = idCmdFirst;
477 do
478 {
479 HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, IndexMenu++, idCmdFirst, idCmdLast, CMF_NORMAL);
480 if (SUCCEEDED(hr))
481 {
482 pEntry->iIdCmdFirst = idCmdFirst;
483 pEntry->NumIds = LOWORD(hr);
484 IndexMenu += pEntry->NumIds;
485 idCmdFirst += pEntry->NumIds + 0x10;
486
487 if(idCmdFirst >= idCmdLast)
488 {
489 /* There is no more room for items */
490 idCmdFirst = idCmdLast;
491 break;
492 }
493 }
494 TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds);
495 pEntry = pEntry->pNext;
496 } while (pEntry);
497
498 m_iIdSHELast = idCmdFirst;
499 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
500 return IndexMenu;
501 }
502
503 UINT
504 CDefaultContextMenu::AddStaticContextMenusToMenu(
505 HMENU hMenu,
506 UINT IndexMenu,
507 UINT iIdCmdFirst,
508 UINT iIdCmdLast)
509 {
510 MENUITEMINFOW mii;
511 UINT idResource;
512 WCHAR wszVerb[40];
513 UINT fState;
514
515 mii.cbSize = sizeof(mii);
516 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
517 mii.fType = MFT_STRING;
518 mii.wID = iIdCmdFirst;
519 mii.dwTypeData = NULL;
520 m_iIdSCMFirst = mii.wID;
521
522 PStaticShellEntry pEntry = m_pStaticEntries;
523
524 while (pEntry)
525 {
526 fState = MFS_ENABLED;
527 mii.dwTypeData = NULL;
528
529 /* set first entry as default */
530 if (pEntry == m_pStaticEntries)
531 fState |= MFS_DEFAULT;
532
533 if (!wcsicmp(pEntry->szVerb, L"open"))
534 {
535 /* override default when open verb is found */
536 fState |= MFS_DEFAULT;
537 idResource = IDS_OPEN_VERB;
538 }
539 else if (!wcsicmp(pEntry->szVerb, L"explore"))
540 idResource = IDS_EXPLORE_VERB;
541 else if (!wcsicmp(pEntry->szVerb, L"runas"))
542 idResource = IDS_RUNAS_VERB;
543 else if (!wcsicmp(pEntry->szVerb, L"edit"))
544 idResource = IDS_EDIT_VERB;
545 else if (!wcsicmp(pEntry->szVerb, L"find"))
546 idResource = IDS_FIND_VERB;
547 else if (!wcsicmp(pEntry->szVerb, L"print"))
548 idResource = IDS_PRINT_VERB;
549 else if (!wcsicmp(pEntry->szVerb, L"printto"))
550 {
551 pEntry = pEntry->pNext;
552 continue;
553 }
554 else
555 idResource = 0;
556
557 /* By default use verb for menu item name */
558 mii.dwTypeData = pEntry->szVerb;
559
560 if (idResource > 0)
561 {
562 if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
563 mii.dwTypeData = wszVerb; /* use translated verb */
564 else
565 ERR("Failed to load string\n");
566 }
567 else
568 {
569 WCHAR wszKey[256];
570 HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
571
572 if (SUCCEEDED(hr))
573 {
574 HKEY hkVerb;
575 DWORD cbVerb = sizeof(wszVerb);
576 LONG res = RegOpenKeyW(pEntry->hkClass, wszKey, &hkVerb);
577 if (res == ERROR_SUCCESS)
578 {
579 res = RegLoadMUIStringW(hkVerb,
580 NULL,
581 wszVerb,
582 cbVerb,
583 NULL,
584 0,
585 NULL);
586 if (res == ERROR_SUCCESS)
587 {
588 /* use description for the menu entry */
589 mii.dwTypeData = wszVerb;
590 }
591
592 RegCloseKey(hkVerb);
593 }
594 }
595 }
596
597 mii.cch = wcslen(mii.dwTypeData);
598 mii.fState = fState;
599 InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
600
601 mii.wID++;
602 pEntry = pEntry->pNext;
603
604 if (mii.wID >= iIdCmdLast)
605 break;
606 }
607
608 m_iIdSCMLast = mii.wID - 1;
609 return IndexMenu;
610 }
611
612 void WINAPI _InsertMenuItemW(
613 HMENU hMenu,
614 UINT indexMenu,
615 BOOL fByPosition,
616 UINT wID,
617 UINT fType,
618 LPCWSTR dwTypeData,
619 UINT fState)
620 {
621 MENUITEMINFOW mii;
622 WCHAR wszText[100];
623
624 ZeroMemory(&mii, sizeof(mii));
625 mii.cbSize = sizeof(mii);
626 if (fType == MFT_SEPARATOR)
627 mii.fMask = MIIM_ID | MIIM_TYPE;
628 else if (fType == MFT_STRING)
629 {
630 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
631 if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
632 {
633 if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
634 mii.dwTypeData = wszText;
635 else
636 {
637 ERR("failed to load string %p\n", dwTypeData);
638 return;
639 }
640 }
641 else
642 mii.dwTypeData = (LPWSTR)dwTypeData;
643 mii.fState = fState;
644 }
645
646 mii.wID = wID;
647 mii.fType = fType;
648 InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
649 }
650
651 HRESULT
652 WINAPI
653 CDefaultContextMenu::QueryContextMenu(
654 HMENU hMenu,
655 UINT IndexMenu,
656 UINT idCmdFirst,
657 UINT idCmdLast,
658 UINT uFlags)
659 {
660 HRESULT hr;
661 UINT idCmdNext = idCmdFirst;
662
663 /* Add a tiny hack to make all the shell happy until we understand how we should handle 0 ids */
664 if (!idCmdNext)
665 idCmdNext = 1;
666
667 TRACE("BuildShellItemContextMenu entered\n");
668
669 /* Load static verbs and shell extensions from registry */
670 for (UINT i = 0; i < m_cKeys; i++)
671 {
672 AddStaticEntriesForKey(m_aKeys[i]);
673 EnumerateDynamicContextHandlerForKey(m_aKeys[i]);
674 }
675
676 /* Add static context menu handlers */
677 IndexMenu = AddStaticContextMenusToMenu(hMenu, IndexMenu, idCmdNext, idCmdLast);
678 if (m_iIdSCMLast && m_iIdSCMFirst != m_iIdSCMLast)
679 idCmdNext = m_iIdSCMLast + 1;
680
681 /* Add dynamic context menu handlers */
682 BOOL bAddSep = FALSE;
683 IndexMenu = InsertMenuItemsOfDynamicContextMenuExtension(hMenu, IndexMenu, idCmdNext, idCmdLast);
684 if (m_iIdSHELast && m_iIdSHELast != m_iIdSHEFirst)
685 idCmdNext = m_iIdSHELast + 1;
686
687 /* Now let the callback add its own items */
688 QCMINFO qcminfo = {hMenu, IndexMenu, idCmdNext, idCmdLast, NULL};
689 if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU, uFlags, &qcminfo)))
690 {
691 m_iIdCBFirst = idCmdNext;
692 m_iIdCBLast = qcminfo.idCmdFirst;
693 idCmdNext = m_iIdCBLast + 1;
694 }
695
696 /* The rest of the items will be added in the end of the menu */
697 IndexMenu = GetMenuItemCount(hMenu);
698
699 if (uFlags & CMF_VERBSONLY)
700 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdNext - idCmdFirst);
701
702 /* If this is a background context menu we are done */
703 if (!m_cidl)
704 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdNext - idCmdFirst);
705
706 /* Get the attributes of the items */
707 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
708 hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
709 if (FAILED_UNEXPECTEDLY(hr))
710 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmdNext - idCmdFirst);
711
712 /* Add the standard menu entries based on the attributes of the items */
713 BOOL bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
714 if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
715 {
716 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
717 if (rfg & SFGAO_CANMOVE)
718 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
719 if (rfg & SFGAO_CANCOPY)
720 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
721 if (bClipboardData)
722 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_PASTE), MFS_ENABLED);
723
724 bAddSep = TRUE;
725 }
726
727 if (rfg & SFGAO_CANLINK)
728 {
729 bAddSep = FALSE;
730 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
731 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
732 }
733
734 if (rfg & SFGAO_CANDELETE)
735 {
736 if (bAddSep)
737 {
738 bAddSep = FALSE;
739 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
740 }
741 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
742 }
743
744 if (rfg & SFGAO_CANRENAME)
745 {
746 if (bAddSep)
747 {
748 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
749 }
750 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
751 bAddSep = TRUE;
752 }
753
754 if (rfg & SFGAO_HASPROPSHEET)
755 {
756 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
757 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
758 }
759
760 return S_OK;
761 }
762
763 HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
764 {
765 HRESULT hr;
766
767 CComPtr<IDataObject> pda;
768 hr = OleGetClipboard(&pda);
769 if (FAILED_UNEXPECTEDLY(hr))
770 return hr;
771
772 FORMATETC formatetc2;
773 STGMEDIUM medium2;
774 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
775
776 DWORD dwKey= 0;
777
778 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
779 {
780 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
781 if (pdwFlag)
782 {
783 if (*pdwFlag == DROPEFFECT_COPY)
784 dwKey = MK_CONTROL;
785 else
786 dwKey = MK_SHIFT;
787 }
788 else {
789 ERR("No drop effect obtained");
790 }
791 GlobalUnlock(medium2.hGlobal);
792 }
793
794 if (bLink)
795 {
796 dwKey = MK_CONTROL|MK_SHIFT;
797 }
798
799 CComPtr<IDropTarget> pdrop;
800 if (m_cidl)
801 hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop));
802 else
803 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
804
805 if (FAILED_UNEXPECTEDLY(hr))
806 return hr;
807
808 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
809
810 TRACE("CP result %x\n", hr);
811 return S_OK;
812 }
813
814 HRESULT
815 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi)
816 {
817 UNIMPLEMENTED;
818 return E_FAIL;
819 }
820
821 HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi)
822 {
823 if (!m_cidl || !m_pDataObj)
824 return E_FAIL;
825
826 CComPtr<IDropTarget> pDT;
827 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
828 if (FAILED_UNEXPECTEDLY(hr))
829 return hr;
830
831 SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
832
833 return S_OK;
834 }
835
836 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi)
837 {
838 if (!m_cidl || !m_pDataObj)
839 return E_FAIL;
840
841 DoDeleteAsync(m_pDataObj, lpcmi->fMask);
842 return S_OK;
843 }
844
845 HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy)
846 {
847 if (!m_cidl || !m_pDataObj)
848 return E_FAIL;
849
850 if (!bCopy)
851 {
852 FORMATETC formatetc;
853 STGMEDIUM medium;
854 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
855 m_pDataObj->GetData(&formatetc, &medium);
856 DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
857 if (pdwFlag)
858 *pdwFlag = DROPEFFECT_MOVE;
859 GlobalUnlock(medium.hGlobal);
860 m_pDataObj->SetData(&formatetc, &medium, TRUE);
861 }
862
863 HRESULT hr = OleSetClipboard(m_pDataObj);
864 if (FAILED_UNEXPECTEDLY(hr))
865 return hr;
866
867 return S_OK;
868 }
869
870 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi)
871 {
872 CComPtr<IShellBrowser> psb;
873 HRESULT hr;
874
875 if (!m_site || !m_cidl)
876 return E_FAIL;
877
878 /* Get a pointer to the shell browser */
879 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
880 if (FAILED_UNEXPECTEDLY(hr))
881 return hr;
882
883 CComPtr<IShellView> lpSV;
884 hr = psb->QueryActiveShellView(&lpSV);
885 if (FAILED_UNEXPECTEDLY(hr))
886 return hr;
887
888 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
889 hr = lpSV->SelectItem(m_apidl[0], selFlags);
890 if (FAILED_UNEXPECTEDLY(hr))
891 return hr;
892
893 return S_OK;
894 }
895
896 HRESULT
897 CDefaultContextMenu::DoProperties(
898 LPCMINVOKECOMMANDINFO lpcmi)
899 {
900 _DoCallback(DFM_INVOKECOMMAND, DFM_CMD_PROPERTIES, NULL);
901
902 return S_OK;
903 }
904
905 // This code is taken from CNewMenu and should be shared between the 2 classes
906 HRESULT
907 CDefaultContextMenu::DoCreateNewFolder(
908 LPCMINVOKECOMMANDINFO lpici)
909 {
910 WCHAR wszPath[MAX_PATH];
911 WCHAR wszName[MAX_PATH];
912 WCHAR wszNewFolder[25];
913 HRESULT hr;
914
915 /* Get folder path */
916 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
917 if (FAILED_UNEXPECTEDLY(hr))
918 return hr;
919
920 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
921 return E_FAIL;
922
923 /* Create the name of the new directory */
924 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
925 return E_FAIL;
926
927 /* Create the new directory and show the appropriate dialog in case of error */
928 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
929 return E_FAIL;
930
931 /* Show and select the new item in the def view */
932 LPITEMIDLIST pidl;
933 PITEMID_CHILD pidlNewItem;
934 CComPtr<IShellView> psv;
935
936 /* Notify the view object about the new item */
937 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, (LPCVOID)wszName, NULL);
938
939 if (!m_site)
940 return S_OK;
941
942 /* Get a pointer to the shell view */
943 hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
944 if (FAILED_UNEXPECTEDLY(hr))
945 return S_OK;
946
947 /* Attempt to get the pidl of the new item */
948 hr = SHILCreateFromPathW(wszName, &pidl, NULL);
949 if (FAILED_UNEXPECTEDLY(hr))
950 return hr;
951
952 pidlNewItem = ILFindLastID(pidl);
953
954 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
955 SVSI_FOCUSED | SVSI_SELECT);
956 if (FAILED_UNEXPECTEDLY(hr))
957 return hr;
958
959 SHFree(pidl);
960
961 return S_OK;
962 }
963
964 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
965 {
966 PDynamicShellEntry pEntry = m_pDynamicEntries;
967
968 while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
969 pEntry = pEntry->pNext;
970
971 if (!pEntry)
972 return NULL;
973
974 if (idCmd < pEntry->iIdCmdFirst || idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
975 return NULL;
976
977 return pEntry;
978 }
979
980 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
981 #define MAX_VERB 260
982
983 BOOL
984 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
985 {
986 WCHAR UnicodeStr[MAX_VERB];
987
988 /* Loop through all the static verbs looking for a match */
989 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
990 {
991 /* We can match both ANSI and unicode strings */
992 if (IsUnicode)
993 {
994 /* The static verbs are ANSI, get a unicode version before doing the compare */
995 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
996 if (!wcscmp(UnicodeStr, (LPWSTR)Verb))
997 {
998 /* Return the Corresponding Id */
999 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1000 return TRUE;
1001 }
1002 }
1003 else
1004 {
1005 if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
1006 {
1007 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1008 return TRUE;
1009 }
1010 }
1011 }
1012
1013 return FALSE;
1014 }
1015
1016 HRESULT
1017 CDefaultContextMenu::DoDynamicShellExtensions(
1018 LPCMINVOKECOMMANDINFO lpcmi)
1019 {
1020 TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1021
1022 UINT idCmd = LOWORD(lpcmi->lpVerb);
1023 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1024 if (!pEntry)
1025 return E_FAIL;
1026
1027 /* invoke the dynamic context menu */
1028 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1029 return pEntry->pCM->InvokeCommand(lpcmi);
1030 }
1031
1032 DWORD
1033 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
1034 {
1035 CComPtr<IShellBrowser> psb;
1036 HWND hwndTree;
1037 LPCWSTR FlagsName;
1038 WCHAR wszKey[256];
1039 HRESULT hr;
1040 DWORD wFlags;
1041 DWORD cbVerb;
1042
1043 if (!m_site)
1044 return 0;
1045
1046 /* Get a pointer to the shell browser */
1047 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1048 if (FAILED_UNEXPECTEDLY(hr))
1049 return 0;
1050
1051 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1052 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1053 FlagsName = L"ExplorerFlags";
1054 else
1055 FlagsName = L"BrowserFlags";
1056
1057 /* Try to get the flag from the verb */
1058 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
1059 if (FAILED_UNEXPECTEDLY(hr))
1060 return 0;
1061
1062 cbVerb = sizeof(wFlags);
1063 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1064 {
1065 return wFlags;
1066 }
1067
1068 return 0;
1069 }
1070
1071 HRESULT
1072 CDefaultContextMenu::TryToBrowse(
1073 LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
1074 {
1075 CComPtr<IShellBrowser> psb;
1076 HRESULT hr;
1077
1078 if (!m_site)
1079 return E_FAIL;
1080
1081 /* Get a pointer to the shell browser */
1082 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1083 if (FAILED_UNEXPECTEDLY(hr))
1084 return 0;
1085
1086 return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags);
1087 }
1088
1089 HRESULT
1090 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1091 {
1092 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1093 if (pidlFull == NULL)
1094 {
1095 return E_FAIL;
1096 }
1097
1098 WCHAR wszPath[MAX_PATH];
1099 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1100
1101 WCHAR wszDir[MAX_PATH];
1102 if (bHasPath)
1103 {
1104 wcscpy(wszDir, wszPath);
1105 PathRemoveFileSpec(wszDir);
1106 }
1107 else
1108 {
1109 SHGetPathFromIDListW(m_pidlFolder, wszDir);
1110 }
1111
1112 SHELLEXECUTEINFOW sei;
1113 ZeroMemory(&sei, sizeof(sei));
1114 sei.cbSize = sizeof(sei);
1115 sei.hwnd = lpcmi->hwnd;
1116 sei.nShow = SW_SHOWNORMAL;
1117 sei.lpVerb = pEntry->szVerb;
1118 sei.lpDirectory = wszDir;
1119 sei.lpIDList = pidlFull;
1120 sei.hkeyClass = pEntry->hkClass;
1121 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1122 if (bHasPath)
1123 {
1124 sei.lpFile = wszPath;
1125 }
1126
1127 ShellExecuteExW(&sei);
1128
1129 ILFree(pidlFull);
1130
1131 return S_OK;
1132 }
1133
1134 HRESULT
1135 CDefaultContextMenu::DoStaticShellExtensions(
1136 LPCMINVOKECOMMANDINFO lpcmi)
1137 {
1138 PStaticShellEntry pEntry = m_pStaticEntries;
1139 INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
1140 HRESULT hr;
1141 UINT i;
1142
1143 while (pEntry && (iCmd--) > 0)
1144 pEntry = pEntry->pNext;
1145
1146 if (iCmd > 0)
1147 return E_FAIL;
1148
1149 /* Get the browse flags to see if we need to browse */
1150 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1151 BOOL bBrowsed = FALSE;
1152
1153 for (i=0; i < m_cidl; i++)
1154 {
1155 /* Check if we need to browse */
1156 if (wFlags > 0)
1157 {
1158 /* In xp if we have browsed, we don't open any more folders.
1159 * In win7 we browse to the first folder we find and
1160 * open new windows for each of the rest of the folders */
1161 if (bBrowsed)
1162 continue;
1163
1164 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1165 if (SUCCEEDED(hr))
1166 {
1167 bBrowsed = TRUE;
1168 continue;
1169 }
1170 }
1171
1172 InvokePidl(lpcmi, m_apidl[i], pEntry);
1173 }
1174
1175 return S_OK;
1176 }
1177
1178 HRESULT
1179 WINAPI
1180 CDefaultContextMenu::InvokeCommand(
1181 LPCMINVOKECOMMANDINFO lpcmi)
1182 {
1183 CMINVOKECOMMANDINFO LocalInvokeInfo;
1184 HRESULT Result;
1185 UINT CmdId;
1186
1187 /* Take a local copy of the fixed members of the
1188 struct as we might need to modify the verb */
1189 LocalInvokeInfo = *lpcmi;
1190
1191 /* Check if this is a string verb */
1192 if (HIWORD(LocalInvokeInfo.lpVerb))
1193 {
1194 /* Get the ID which corresponds to this verb, and update our local copy */
1195 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1196 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1197 }
1198
1199 /* Check if this is a Id */
1200 switch (LOWORD(LocalInvokeInfo.lpVerb))
1201 {
1202 case FCIDM_SHVIEW_INSERT:
1203 Result = DoPaste(&LocalInvokeInfo, FALSE);
1204 break;
1205 case FCIDM_SHVIEW_INSERTLINK:
1206 Result = DoPaste(&LocalInvokeInfo, TRUE);
1207 break;
1208 case FCIDM_SHVIEW_OPEN:
1209 case FCIDM_SHVIEW_EXPLORE:
1210 Result = DoOpenOrExplore(&LocalInvokeInfo);
1211 break;
1212 case FCIDM_SHVIEW_COPY:
1213 case FCIDM_SHVIEW_CUT:
1214 Result = DoCopyOrCut(&LocalInvokeInfo, LOWORD(LocalInvokeInfo.lpVerb) == FCIDM_SHVIEW_COPY);
1215 break;
1216 case FCIDM_SHVIEW_CREATELINK:
1217 Result = DoCreateLink(&LocalInvokeInfo);
1218 break;
1219 case FCIDM_SHVIEW_DELETE:
1220 Result = DoDelete(&LocalInvokeInfo);
1221 break;
1222 case FCIDM_SHVIEW_RENAME:
1223 Result = DoRename(&LocalInvokeInfo);
1224 break;
1225 case FCIDM_SHVIEW_PROPERTIES:
1226 Result = DoProperties(&LocalInvokeInfo);
1227 break;
1228 case FCIDM_SHVIEW_NEWFOLDER:
1229 Result = DoCreateNewFolder(&LocalInvokeInfo);
1230 break;
1231 default:
1232 Result = E_UNEXPECTED;
1233 break;
1234 }
1235
1236 /* Check for ID's we didn't find a handler for */
1237 if (Result == E_UNEXPECTED)
1238 {
1239 if (m_pDynamicEntries)
1240 {
1241 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSHEFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSHELast)
1242 Result = DoDynamicShellExtensions(&LocalInvokeInfo);
1243 }
1244
1245 if (m_pStaticEntries)
1246 {
1247 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSCMFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSCMLast)
1248 Result = DoStaticShellExtensions(&LocalInvokeInfo);
1249 }
1250
1251 if (m_iIdCBFirst != m_iIdCBLast)
1252 {
1253 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdCBFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdCBLast)
1254 Result = _DoCallback(DFM_INVOKECOMMAND, LOWORD(LocalInvokeInfo.lpVerb), NULL);
1255 }
1256 }
1257
1258 if (Result == E_UNEXPECTED)
1259 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1260
1261 return Result;
1262 }
1263
1264 HRESULT
1265 WINAPI
1266 CDefaultContextMenu::GetCommandString(
1267 UINT_PTR idCommand,
1268 UINT uFlags,
1269 UINT* lpReserved,
1270 LPSTR lpszName,
1271 UINT uMaxNameLen)
1272 {
1273 /* We don't handle the help text yet */
1274 if (uFlags == GCS_HELPTEXTA ||
1275 uFlags == GCS_HELPTEXTW)
1276 {
1277 return E_NOTIMPL;
1278 }
1279
1280 /* Loop looking for a matching Id */
1281 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1282 {
1283 if (g_StaticInvokeCmdMap[i].IntVerb == idCommand)
1284 {
1285 /* Validation just returns S_OK on a match */
1286 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1287 return S_OK;
1288
1289 /* Return a copy of the ANSI verb */
1290 if (uFlags == GCS_VERBA)
1291 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1292
1293 /* Convert the ANSI verb to unicode and return that */
1294 if (uFlags == GCS_VERBW)
1295 {
1296 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1297 return S_OK;
1298 }
1299 }
1300 }
1301
1302 return E_INVALIDARG;
1303 }
1304
1305 HRESULT
1306 WINAPI
1307 CDefaultContextMenu::HandleMenuMsg(
1308 UINT uMsg,
1309 WPARAM wParam,
1310 LPARAM lParam)
1311 {
1312 /* FIXME: Should we implement this as well? */
1313 return S_OK;
1314 }
1315
1316 HRESULT
1317 WINAPI
1318 CDefaultContextMenu::HandleMenuMsg2(
1319 UINT uMsg,
1320 WPARAM wParam,
1321 LPARAM lParam,
1322 LRESULT *plResult)
1323 {
1324 switch (uMsg)
1325 {
1326 case WM_INITMENUPOPUP:
1327 {
1328 PDynamicShellEntry pEntry = m_pDynamicEntries;
1329 while (pEntry)
1330 {
1331 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1332 pEntry = pEntry->pNext;
1333 }
1334 break;
1335 }
1336 case WM_DRAWITEM:
1337 {
1338 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1339 PDynamicShellEntry pEntry = GetDynamicEntry(pDrawStruct->itemID);
1340 if (pEntry)
1341 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1342 break;
1343 }
1344 case WM_MEASUREITEM:
1345 {
1346 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1347 PDynamicShellEntry pEntry = GetDynamicEntry(pMeasureStruct->itemID);
1348 if (pEntry)
1349 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1350 break;
1351 }
1352 case WM_MENUCHAR :
1353 /* FIXME */
1354 break;
1355 default:
1356 ERR("Got unknown message:%d\n", uMsg);
1357 }
1358 return S_OK;
1359 }
1360
1361 HRESULT
1362 WINAPI
1363 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1364 {
1365 m_site = pUnkSite;
1366 return S_OK;
1367 }
1368
1369 HRESULT
1370 WINAPI
1371 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1372 {
1373 if (!m_site)
1374 return E_FAIL;
1375
1376 return m_site->QueryInterface(riid, ppvSite);
1377 }
1378
1379 static
1380 HRESULT
1381 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1382 {
1383 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1384 }
1385
1386 /*************************************************************************
1387 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1388 *
1389 */
1390
1391 HRESULT
1392 WINAPI
1393 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1394 {
1395 HRESULT hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1396 if (FAILED_UNEXPECTEDLY(hr))
1397 return hr;
1398
1399 return S_OK;
1400 }
1401
1402 /*************************************************************************
1403 * CDefFolderMenu_Create2 [SHELL32.701]
1404 *
1405 */
1406
1407 HRESULT
1408 WINAPI
1409 CDefFolderMenu_Create2(
1410 PCIDLIST_ABSOLUTE pidlFolder,
1411 HWND hwnd,
1412 UINT cidl,
1413 PCUITEMID_CHILD_ARRAY apidl,
1414 IShellFolder *psf,
1415 LPFNDFMCALLBACK lpfn,
1416 UINT nKeys,
1417 const HKEY *ahkeyClsKeys,
1418 IContextMenu **ppcm)
1419 {
1420 DEFCONTEXTMENU dcm;
1421 dcm.hwnd = hwnd;
1422 dcm.pcmcb = NULL;
1423 dcm.pidlFolder = pidlFolder;
1424 dcm.psf = psf;
1425 dcm.cidl = cidl;
1426 dcm.apidl = apidl;
1427 dcm.punkAssociationInfo = NULL;
1428 dcm.cKeys = nKeys;
1429 dcm.aKeys = ahkeyClsKeys;
1430
1431 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1432 if (FAILED_UNEXPECTEDLY(hr))
1433 return hr;
1434
1435 return S_OK;
1436 }