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