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