[SHELL32]
[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 this is a background context menu we are done */
677 if (!m_cidl)
678 return S_OK;
679
680 /* Get the attributes of the items */
681 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
682 hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
683 if (FAILED_UNEXPECTEDLY(hr))
684 return S_OK;
685
686 /* Add the standard menu entries based on the attributes of the items */
687 BOOL bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
688 if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
689 {
690 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
691 if (rfg & SFGAO_CANMOVE)
692 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
693 if (rfg & SFGAO_CANCOPY)
694 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
695 if (bClipboardData)
696 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_PASTE), MFS_ENABLED);
697
698 bAddSep = TRUE;
699 }
700
701 if (rfg & SFGAO_CANLINK)
702 {
703 bAddSep = FALSE;
704 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
705 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
706 }
707
708 if (rfg & SFGAO_CANDELETE)
709 {
710 if (bAddSep)
711 {
712 bAddSep = FALSE;
713 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
714 }
715 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
716 }
717
718 if (rfg & SFGAO_CANRENAME)
719 {
720 if (bAddSep)
721 {
722 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
723 }
724 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
725 bAddSep = TRUE;
726 }
727
728 if (rfg & SFGAO_HASPROPSHEET)
729 {
730 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
731 _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
732 }
733
734 return S_OK;
735 }
736
737 HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
738 {
739 HRESULT hr;
740
741 CComPtr<IDataObject> pda;
742 hr = OleGetClipboard(&pda);
743 if (FAILED_UNEXPECTEDLY(hr))
744 return hr;
745
746 FORMATETC formatetc2;
747 STGMEDIUM medium2;
748 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
749
750 DWORD dwKey= 0;
751
752 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
753 {
754 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
755 if (pdwFlag)
756 {
757 if (*pdwFlag == DROPEFFECT_COPY)
758 dwKey = MK_CONTROL;
759 else
760 dwKey = MK_SHIFT;
761 }
762 else {
763 ERR("No drop effect obtained");
764 }
765 GlobalUnlock(medium2.hGlobal);
766 }
767
768 if (bLink)
769 {
770 dwKey = MK_CONTROL|MK_SHIFT;
771 }
772
773 CComPtr<IDropTarget> pdrop;
774 if (m_cidl)
775 hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop));
776 else
777 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
778
779 if (FAILED_UNEXPECTEDLY(hr))
780 return hr;
781
782 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
783
784 TRACE("CP result %x\n", hr);
785 return S_OK;
786 }
787
788 HRESULT
789 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi)
790 {
791 UNIMPLEMENTED;
792 return E_FAIL;
793 }
794
795 HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi)
796 {
797 if (!m_cidl || !m_pDataObj)
798 return E_FAIL;
799
800 CComPtr<IDropTarget> pDT;
801 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
802 if (FAILED_UNEXPECTEDLY(hr))
803 return hr;
804
805 SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
806
807 return S_OK;
808 }
809
810 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi)
811 {
812 if (!m_cidl || !m_pDataObj)
813 return E_FAIL;
814
815 DoDeleteAsync(m_pDataObj, lpcmi->fMask);
816 return S_OK;
817 }
818
819 HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy)
820 {
821 if (!m_cidl || !m_pDataObj)
822 return E_FAIL;
823
824 if (!bCopy)
825 {
826 FORMATETC formatetc;
827 STGMEDIUM medium;
828 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
829 m_pDataObj->GetData(&formatetc, &medium);
830 DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
831 if (pdwFlag)
832 *pdwFlag = DROPEFFECT_MOVE;
833 GlobalUnlock(medium.hGlobal);
834 m_pDataObj->SetData(&formatetc, &medium, TRUE);
835 }
836
837 HRESULT hr = OleSetClipboard(m_pDataObj);
838 if (FAILED_UNEXPECTEDLY(hr))
839 return hr;
840
841 return S_OK;
842 }
843
844 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi)
845 {
846 CComPtr<IShellBrowser> psb;
847 HRESULT hr;
848
849 if (!m_site || !m_cidl)
850 return E_FAIL;
851
852 /* Get a pointer to the shell browser */
853 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
854 if (FAILED_UNEXPECTEDLY(hr))
855 return hr;
856
857 CComPtr<IShellView> lpSV;
858 hr = psb->QueryActiveShellView(&lpSV);
859 if (FAILED_UNEXPECTEDLY(hr))
860 return hr;
861
862 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
863 hr = lpSV->SelectItem(m_apidl[0], selFlags);
864 if (FAILED_UNEXPECTEDLY(hr))
865 return hr;
866
867 return S_OK;
868 }
869
870 HRESULT
871 CDefaultContextMenu::DoProperties(
872 LPCMINVOKECOMMANDINFO lpcmi)
873 {
874 _DoCallback(DFM_INVOKECOMMAND, DFM_CMD_PROPERTIES, NULL);
875
876 return S_OK;
877 }
878
879 // This code is taken from CNewMenu and should be shared between the 2 classes
880 HRESULT
881 CDefaultContextMenu::DoCreateNewFolder(
882 LPCMINVOKECOMMANDINFO lpici)
883 {
884 WCHAR wszPath[MAX_PATH];
885 WCHAR wszName[MAX_PATH];
886 WCHAR wszNewFolder[25];
887 HRESULT hr;
888
889 /* Get folder path */
890 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
891 if (FAILED_UNEXPECTEDLY(hr))
892 return hr;
893
894 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
895 return E_FAIL;
896
897 /* Create the name of the new directory */
898 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
899 return E_FAIL;
900
901 /* Create the new directory and show the appropriate dialog in case of error */
902 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
903 return E_FAIL;
904
905 /* Show and select the new item in the def view */
906 LPITEMIDLIST pidl;
907 PITEMID_CHILD pidlNewItem;
908 CComPtr<IShellView> psv;
909
910 /* Notify the view object about the new item */
911 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, (LPCVOID)wszName, NULL);
912
913 if (!m_site)
914 return S_OK;
915
916 /* Get a pointer to the shell view */
917 hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
918 if (FAILED_UNEXPECTEDLY(hr))
919 return S_OK;
920
921 /* Attempt to get the pidl of the new item */
922 hr = SHILCreateFromPathW(wszName, &pidl, NULL);
923 if (FAILED_UNEXPECTEDLY(hr))
924 return hr;
925
926 pidlNewItem = ILFindLastID(pidl);
927
928 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
929 SVSI_FOCUSED | SVSI_SELECT);
930 if (FAILED_UNEXPECTEDLY(hr))
931 return hr;
932
933 SHFree(pidl);
934
935 return S_OK;
936 }
937
938 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
939 {
940 PDynamicShellEntry pEntry = m_pDynamicEntries;
941
942 while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
943 pEntry = pEntry->pNext;
944
945 if (!pEntry)
946 return NULL;
947
948 if (idCmd < pEntry->iIdCmdFirst || idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
949 return NULL;
950
951 return pEntry;
952 }
953
954 //FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
955 #define MAX_VERB 260
956
957 BOOL
958 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
959 {
960 WCHAR UnicodeStr[MAX_VERB];
961
962 /* Loop through all the static verbs looking for a match */
963 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
964 {
965 /* We can match both ANSI and unicode strings */
966 if (IsUnicode)
967 {
968 /* The static verbs are ANSI, get a unicode version before doing the compare */
969 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
970 if (!wcscmp(UnicodeStr, (LPWSTR)Verb))
971 {
972 /* Return the Corresponding Id */
973 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
974 return TRUE;
975 }
976 }
977 else
978 {
979 if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
980 {
981 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
982 return TRUE;
983 }
984 }
985 }
986
987 return FALSE;
988 }
989
990 HRESULT
991 CDefaultContextMenu::DoDynamicShellExtensions(
992 LPCMINVOKECOMMANDINFO lpcmi)
993 {
994 TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
995
996 UINT idCmd = LOWORD(lpcmi->lpVerb);
997 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
998 if (!pEntry)
999 return E_FAIL;
1000
1001 /* invoke the dynamic context menu */
1002 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1003 return pEntry->pCM->InvokeCommand(lpcmi);
1004 }
1005
1006 DWORD
1007 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
1008 {
1009 CComPtr<IShellBrowser> psb;
1010 HWND hwndTree;
1011 LPCWSTR FlagsName;
1012 WCHAR wszKey[256];
1013 HRESULT hr;
1014 DWORD wFlags;
1015 DWORD cbVerb;
1016
1017 if (!m_site)
1018 return 0;
1019
1020 /* Get a pointer to the shell browser */
1021 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1022 if (FAILED_UNEXPECTEDLY(hr))
1023 return 0;
1024
1025 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1026 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1027 FlagsName = L"ExplorerFlags";
1028 else
1029 FlagsName = L"BrowserFlags";
1030
1031 /* Try to get the flag from the verb */
1032 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
1033 if (FAILED_UNEXPECTEDLY(hr))
1034 return 0;
1035
1036 cbVerb = sizeof(wFlags);
1037 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1038 {
1039 return wFlags;
1040 }
1041
1042 return 0;
1043 }
1044
1045 HRESULT
1046 CDefaultContextMenu::TryToBrowse(
1047 LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
1048 {
1049 CComPtr<IShellBrowser> psb;
1050 HRESULT hr;
1051
1052 if (!m_site)
1053 return E_FAIL;
1054
1055 /* Get a pointer to the shell browser */
1056 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1057 if (FAILED_UNEXPECTEDLY(hr))
1058 return 0;
1059
1060 return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags);
1061 }
1062
1063 HRESULT
1064 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1065 {
1066 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1067 if (pidlFull == NULL)
1068 {
1069 return E_FAIL;
1070 }
1071
1072 WCHAR wszPath[MAX_PATH];
1073 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1074
1075 WCHAR wszDir[MAX_PATH];
1076 if (bHasPath)
1077 {
1078 wcscpy(wszDir, wszPath);
1079 PathRemoveFileSpec(wszDir);
1080 }
1081 else
1082 {
1083 SHGetPathFromIDListW(m_pidlFolder, wszDir);
1084 }
1085
1086 SHELLEXECUTEINFOW sei;
1087 ZeroMemory(&sei, sizeof(sei));
1088 sei.cbSize = sizeof(sei);
1089 sei.hwnd = lpcmi->hwnd;
1090 sei.nShow = SW_SHOWNORMAL;
1091 sei.lpVerb = pEntry->szVerb;
1092 sei.lpDirectory = wszDir;
1093 sei.lpIDList = pidlFull;
1094 sei.hkeyClass = pEntry->hkClass;
1095 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1096 if (bHasPath)
1097 {
1098 sei.lpFile = wszPath;
1099 }
1100
1101 ShellExecuteExW(&sei);
1102
1103 ILFree(pidlFull);
1104
1105 return S_OK;
1106 }
1107
1108 HRESULT
1109 CDefaultContextMenu::DoStaticShellExtensions(
1110 LPCMINVOKECOMMANDINFO lpcmi)
1111 {
1112 PStaticShellEntry pEntry = m_pStaticEntries;
1113 INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
1114 HRESULT hr;
1115 UINT i;
1116
1117 while (pEntry && (iCmd--) > 0)
1118 pEntry = pEntry->pNext;
1119
1120 if (iCmd > 0)
1121 return E_FAIL;
1122
1123 /* Get the browse flags to see if we need to browse */
1124 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1125 BOOL bBrowsed = FALSE;
1126
1127 for (i=0; i < m_cidl; i++)
1128 {
1129 /* Check if we need to browse */
1130 if (wFlags > 0)
1131 {
1132 /* In xp if we have browsed, we don't open any more folders .
1133 * In win7 we browse to the first folder we find and
1134 * open new windows fo for each of the rest of the folders */
1135 if (bBrowsed)
1136 continue;
1137
1138 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1139 if (SUCCEEDED(hr))
1140 {
1141 bBrowsed = TRUE;
1142 continue;
1143 }
1144 }
1145
1146 InvokePidl(lpcmi, m_apidl[i], pEntry);
1147 }
1148
1149 return S_OK;
1150 }
1151
1152 HRESULT
1153 WINAPI
1154 CDefaultContextMenu::InvokeCommand(
1155 LPCMINVOKECOMMANDINFO lpcmi)
1156 {
1157 CMINVOKECOMMANDINFO LocalInvokeInfo;
1158 HRESULT Result;
1159 UINT CmdId;
1160
1161 /* Take a local copy of the fixed members of the
1162 struct as we might need to modify the verb */
1163 LocalInvokeInfo = *lpcmi;
1164
1165 /* Check if this is a string verb */
1166 if (HIWORD(LocalInvokeInfo.lpVerb))
1167 {
1168 /* Get the ID which corresponds to this verb, and update our local copy */
1169 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1170 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1171 }
1172
1173 /* Check if this is a Id */
1174 switch (LOWORD(LocalInvokeInfo.lpVerb))
1175 {
1176 case FCIDM_SHVIEW_INSERT:
1177 Result = DoPaste(&LocalInvokeInfo, FALSE);
1178 break;
1179 case FCIDM_SHVIEW_INSERTLINK:
1180 Result = DoPaste(&LocalInvokeInfo, TRUE);
1181 break;
1182 case FCIDM_SHVIEW_OPEN:
1183 case FCIDM_SHVIEW_EXPLORE:
1184 Result = DoOpenOrExplore(&LocalInvokeInfo);
1185 break;
1186 case FCIDM_SHVIEW_COPY:
1187 case FCIDM_SHVIEW_CUT:
1188 Result = DoCopyOrCut(&LocalInvokeInfo, LOWORD(LocalInvokeInfo.lpVerb) == FCIDM_SHVIEW_COPY);
1189 break;
1190 case FCIDM_SHVIEW_CREATELINK:
1191 Result = DoCreateLink(&LocalInvokeInfo);
1192 break;
1193 case FCIDM_SHVIEW_DELETE:
1194 Result = DoDelete(&LocalInvokeInfo);
1195 break;
1196 case FCIDM_SHVIEW_RENAME:
1197 Result = DoRename(&LocalInvokeInfo);
1198 break;
1199 case FCIDM_SHVIEW_PROPERTIES:
1200 Result = DoProperties(&LocalInvokeInfo);
1201 break;
1202 case FCIDM_SHVIEW_NEWFOLDER:
1203 Result = DoCreateNewFolder(&LocalInvokeInfo);
1204 break;
1205 default:
1206
1207 _DoCallback(DFM_INVOKECOMMAND, LOWORD(LocalInvokeInfo.lpVerb), NULL);
1208
1209 Result = E_UNEXPECTED;
1210 break;
1211 }
1212
1213 /* Check for ID's we didn't find a handler for */
1214 if (Result == E_UNEXPECTED)
1215 {
1216 if (m_iIdSHEFirst && m_iIdSHELast)
1217 {
1218 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSHEFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSHELast)
1219 Result = DoDynamicShellExtensions(&LocalInvokeInfo);
1220 }
1221
1222 if (m_iIdSCMFirst && m_iIdSCMLast)
1223 {
1224 if (LOWORD(LocalInvokeInfo.lpVerb) >= m_iIdSCMFirst && LOWORD(LocalInvokeInfo.lpVerb) <= m_iIdSCMLast)
1225 Result = DoStaticShellExtensions(&LocalInvokeInfo);
1226 }
1227 }
1228
1229 if (Result == E_UNEXPECTED)
1230 FIXME("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1231
1232 return Result;
1233 }
1234
1235 HRESULT
1236 WINAPI
1237 CDefaultContextMenu::GetCommandString(
1238 UINT_PTR idCommand,
1239 UINT uFlags,
1240 UINT* lpReserved,
1241 LPSTR lpszName,
1242 UINT uMaxNameLen)
1243 {
1244 /* We don't handle the help text yet */
1245 if (uFlags == GCS_HELPTEXTA ||
1246 uFlags == GCS_HELPTEXTW)
1247 {
1248 return E_NOTIMPL;
1249 }
1250
1251 /* Loop looking for a matching Id */
1252 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1253 {
1254 if (g_StaticInvokeCmdMap[i].IntVerb == idCommand)
1255 {
1256 /* Validation just returns S_OK on a match */
1257 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1258 return S_OK;
1259
1260 /* Return a copy of the ANSI verb */
1261 if (uFlags == GCS_VERBA)
1262 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1263
1264 /* Convert the ANSI verb to unicode and return that */
1265 if (uFlags == GCS_VERBW)
1266 {
1267 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1268 return S_OK;
1269 }
1270 }
1271 }
1272
1273 return E_INVALIDARG;
1274 }
1275
1276 HRESULT
1277 WINAPI
1278 CDefaultContextMenu::HandleMenuMsg(
1279 UINT uMsg,
1280 WPARAM wParam,
1281 LPARAM lParam)
1282 {
1283 /* FIXME: Should we implement this as well? */
1284 return S_OK;
1285 }
1286
1287 HRESULT
1288 WINAPI
1289 CDefaultContextMenu::HandleMenuMsg2(
1290 UINT uMsg,
1291 WPARAM wParam,
1292 LPARAM lParam,
1293 LRESULT *plResult)
1294 {
1295 switch (uMsg)
1296 {
1297 case WM_INITMENUPOPUP:
1298 {
1299 PDynamicShellEntry pEntry = m_pDynamicEntries;
1300 while (pEntry)
1301 {
1302 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1303 pEntry = pEntry->pNext;
1304 }
1305 break;
1306 }
1307 case WM_DRAWITEM:
1308 {
1309 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1310 PDynamicShellEntry pEntry = GetDynamicEntry(pDrawStruct->itemID);
1311 if (pEntry)
1312 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1313 break;
1314 }
1315 case WM_MEASUREITEM:
1316 {
1317 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1318 PDynamicShellEntry pEntry = GetDynamicEntry(pMeasureStruct->itemID);
1319 if (pEntry)
1320 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1321 break;
1322 }
1323 case WM_MENUCHAR :
1324 /* FIXME */
1325 break;
1326 default:
1327 ERR("Got unknown message:%d\n", uMsg);
1328 }
1329 return S_OK;
1330 }
1331
1332 HRESULT
1333 WINAPI
1334 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1335 {
1336 m_site = pUnkSite;
1337 return S_OK;
1338 }
1339
1340 HRESULT
1341 WINAPI
1342 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1343 {
1344 if (!m_site)
1345 return E_FAIL;
1346
1347 return m_site->QueryInterface(riid, ppvSite);
1348 }
1349
1350 static
1351 HRESULT
1352 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1353 {
1354 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1355 }
1356
1357 /*************************************************************************
1358 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1359 *
1360 */
1361
1362 HRESULT
1363 WINAPI
1364 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1365 {
1366 HRESULT hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1367 if (FAILED_UNEXPECTEDLY(hr))
1368 return hr;
1369
1370 return S_OK;
1371 }
1372
1373 /*************************************************************************
1374 * CDefFolderMenu_Create2 [SHELL32.701]
1375 *
1376 */
1377
1378 HRESULT
1379 WINAPI
1380 CDefFolderMenu_Create2(
1381 PCIDLIST_ABSOLUTE pidlFolder,
1382 HWND hwnd,
1383 UINT cidl,
1384 PCUITEMID_CHILD_ARRAY apidl,
1385 IShellFolder *psf,
1386 LPFNDFMCALLBACK lpfn,
1387 UINT nKeys,
1388 const HKEY *ahkeyClsKeys,
1389 IContextMenu **ppcm)
1390 {
1391 DEFCONTEXTMENU dcm;
1392 dcm.hwnd = hwnd;
1393 dcm.pcmcb = NULL;
1394 dcm.pidlFolder = pidlFolder;
1395 dcm.psf = psf;
1396 dcm.cidl = cidl;
1397 dcm.apidl = apidl;
1398 dcm.punkAssociationInfo = NULL;
1399 dcm.cKeys = nKeys;
1400 dcm.aKeys = ahkeyClsKeys;
1401
1402 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1403 if (FAILED_UNEXPECTEDLY(hr))
1404 return hr;
1405
1406 return S_OK;
1407 }