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