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