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