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