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