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