71c25a7d407ce6cb458d6cf5175ff462ea95a8e2
[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 }
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"runas"))
648 idResource = IDS_RUNAS_VERB;
649 else if (!wcsicmp(curEntry->szVerb, L"edit"))
650 idResource = IDS_EDIT_VERB;
651 else if (!wcsicmp(curEntry->szVerb, L"find"))
652 idResource = IDS_FIND_VERB;
653 else if (!wcsicmp(curEntry->szVerb, L"print"))
654 idResource = IDS_PRINT_VERB;
655 else if (!wcsicmp(curEntry->szVerb, L"play"))
656 idResource = IDS_PLAY_VERB;
657 else if (!wcsicmp(curEntry->szVerb, L"preview"))
658 idResource = IDS_PREVIEW_VERB;
659 else
660 idResource = 0;
661
662 if (idResource > 0)
663 {
664 if (LoadStringW(shell32_hInstance, idResource, szVerb, sizeof(szVerb)/sizeof(WCHAR)))
665 {
666 /* use translated verb */
667 szVerb[(sizeof(szVerb)/sizeof(WCHAR))-1] = L'\0';
668 mii.dwTypeData = szVerb;
669 }
670 }
671 else
672 {
673 Length = wcslen(curEntry->szClass) + wcslen(curEntry->szVerb) + 8;
674 if (Length < sizeof(szTemp)/sizeof(WCHAR))
675 {
676 wcscpy(szTemp, curEntry->szClass);
677 wcscat(szTemp, L"\\shell\\");
678 wcscat(szTemp, curEntry->szVerb);
679 dwSize = sizeof(szVerb);
680
681 if (RegGetValueW(HKEY_CLASSES_ROOT, szTemp, NULL, RRF_RT_REG_SZ, NULL, szVerb, &dwSize) == ERROR_SUCCESS)
682 {
683 /* use description for the menu entry */
684 mii.dwTypeData = szVerb;
685 }
686 else
687 {
688 /* use verb for the menu entry */
689 mii.dwTypeData = curEntry->szVerb;
690 }
691 }
692
693 }
694
695 mii.cch = wcslen(mii.dwTypeData);
696 mii.fState = fState;
697 InsertMenuItemW(hMenu, indexMenu++, TRUE, &mii);
698
699 mii.wID++;
700 curEntry = curEntry->Next;
701 }
702 This->iIdSCMLast = mii.wID - 1;
703 return indexMenu;
704 }
705
706 void WINAPI _InsertMenuItemW (
707 HMENU hmenu,
708 UINT indexMenu,
709 BOOL fByPosition,
710 UINT wID,
711 UINT fType,
712 LPCWSTR dwTypeData,
713 UINT fState)
714 {
715 MENUITEMINFOW mii;
716 WCHAR szText[100];
717
718 ZeroMemory(&mii, sizeof(mii));
719 mii.cbSize = sizeof(mii);
720 if (fType == MFT_SEPARATOR)
721 {
722 mii.fMask = MIIM_ID | MIIM_TYPE;
723 }
724 else if (fType == MFT_STRING)
725 {
726 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
727 if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
728 {
729 if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), szText, sizeof(szText)/sizeof(WCHAR)))
730 {
731 szText[(sizeof(szText)/sizeof(WCHAR))-1] = 0;
732 mii.dwTypeData = szText;
733 }
734 else
735 {
736 TRACE("failed to load string %p\n", dwTypeData);
737 return;
738 }
739 }
740 else
741 {
742 mii.dwTypeData = (LPWSTR) dwTypeData;
743 }
744 mii.fState = fState;
745 }
746
747 mii.wID = wID;
748 mii.fType = fType;
749 InsertMenuItemW( hmenu, indexMenu, fByPosition, &mii);
750 }
751
752 UINT
753 BuildShellItemContextMenu(
754 IDefaultContextMenuImpl * This,
755 HMENU hMenu,
756 UINT iIdCmdFirst,
757 UINT iIdCmdLast,
758 UINT uFlags)
759 {
760 WCHAR szPath[MAX_PATH];
761 WCHAR szTemp[40];
762 HKEY hKey;
763 UINT indexMenu;
764 SFGAOF rfg;
765 HRESULT hr;
766 BOOL bAddSep;
767 GUID * guid;
768 BOOL bClipboardData;
769 STRRET strFile;
770 LPWSTR pOffset;
771 DWORD dwSize;
772
773 TRACE("BuildShellItemContextMenu entered\n");
774
775 if (IShellFolder2_GetDisplayNameOf(This->dcm.psf, This->dcm.apidl[0], SHGDN_FORPARSING, &strFile) == S_OK)
776 {
777 if (StrRetToBufW(&strFile, This->dcm.apidl[0], szPath, MAX_PATH) == S_OK)
778 {
779 pOffset = wcsrchr(szPath, L'.');
780 if (pOffset)
781 {
782 /* enumerate dynamic/static for a given file class */
783 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pOffset, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
784 {
785 /* add static verbs */
786 SH_AddStaticEntryForFileClass(This, pOffset);
787 /* load dynamic extensions from file extension key */
788 EnumerateDynamicContextHandlerForKey(This, hKey);
789 RegCloseKey(hKey);
790 }
791 dwSize = sizeof(szTemp);
792 if (RegGetValueW(HKEY_CLASSES_ROOT, pOffset, NULL, RRF_RT_REG_SZ, NULL, szTemp, &dwSize) == ERROR_SUCCESS)
793 {
794 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
795 {
796 /* add static verbs from progid key */
797 SH_AddStaticEntryForFileClass(This, szTemp);
798 /* load dynamic extensions from progid key */
799 EnumerateDynamicContextHandlerForKey(This, hKey);
800 RegCloseKey(hKey);
801 }
802 }
803 }
804 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"*", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
805 {
806 /* load default extensions */
807 EnumerateDynamicContextHandlerForKey(This, hKey);
808 RegCloseKey(hKey);
809 }
810 }
811 }
812
813 guid = _ILGetGUIDPointer(This->dcm.apidl[0]);
814 if (guid)
815 {
816 LPOLESTR pwszCLSID;
817 WCHAR buffer[60];
818
819 wcscpy(buffer, L"CLSID\\");
820 hr = StringFromCLSID(guid, &pwszCLSID);
821 if (hr == S_OK)
822 {
823 wcscpy(&buffer[6], pwszCLSID);
824 TRACE("buffer %s\n", debugstr_w(buffer));
825 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, buffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
826 {
827 EnumerateDynamicContextHandlerForKey(This, hKey);
828 SH_AddStaticEntryForFileClass(This, buffer);
829 RegCloseKey(hKey);
830 }
831 CoTaskMemFree(pwszCLSID);
832 }
833 }
834
835
836 if (_ILIsDrive(This->dcm.apidl[0]))
837 {
838 SH_AddStaticEntryForFileClass(This, L"Drive");
839 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Drive", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
840 {
841 EnumerateDynamicContextHandlerForKey(This, hKey);
842 RegCloseKey(hKey);
843 }
844
845 }
846
847 /* add static actions */
848 rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
849 hr = IShellFolder_GetAttributesOf(This->dcm.psf, This->dcm.cidl, This->dcm.apidl, &rfg);
850 if (!SUCCEEDED(hr))
851 rfg = 0;
852
853 if (rfg & SFGAO_FOLDER)
854 {
855 /* add the default verbs open / explore */
856 SH_AddStaticEntryForFileClass(This, L"Folder");
857 SH_AddStaticEntryForFileClass(This, L"Directory");
858 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Folder", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
859 {
860 EnumerateDynamicContextHandlerForKey(This, hKey);
861 RegCloseKey(hKey);
862 }
863 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
864 {
865 EnumerateDynamicContextHandlerForKey(This, hKey);
866 RegCloseKey(hKey);
867 }
868 }
869
870
871 if (rfg & SFGAO_FILESYSTEM)
872 {
873 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"AllFilesystemObjects", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
874 {
875 /* sendto service is registered here */
876 EnumerateDynamicContextHandlerForKey(This, hKey);
877 RegCloseKey(hKey);
878 }
879 }
880
881 /* add static context menu handlers */
882 indexMenu = AddStaticContextMenusToMenu(hMenu, 0, This);
883 /* now process dynamic context menu handlers */
884 indexMenu = InsertMenuItemsOfDynamicContextMenuExtension(This, hMenu, indexMenu, iIdCmdFirst, iIdCmdLast);
885 TRACE("indexMenu %d\n", indexMenu);
886
887 if (_ILIsDrive(This->dcm.apidl[0]))
888 {
889 /* The 'Format' option must be always available,
890 * thus it is not registered as a static shell extension
891 */
892 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
893 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0x7ABC, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
894 bAddSep = TRUE;
895 }
896
897 bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
898 if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
899 {
900 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
901 if (rfg & SFGAO_CANMOVE)
902 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
903 if (rfg & SFGAO_CANCOPY)
904 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
905 if (bClipboardData)
906 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_INSERT), MFS_ENABLED);
907
908 bAddSep = TRUE;
909 }
910
911
912 if (rfg & SFGAO_CANLINK)
913 {
914 bAddSep = FALSE;
915 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
916 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
917 }
918
919
920 if (rfg & SFGAO_CANDELETE)
921 {
922 if (bAddSep)
923 {
924 bAddSep = FALSE;
925 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
926 }
927 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
928 }
929
930 if (rfg & SFGAO_CANRENAME)
931 {
932 if (bAddSep)
933 {
934 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
935 }
936 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
937 bAddSep = TRUE;
938 }
939
940 if (rfg & SFGAO_HASPROPSHEET)
941 {
942 _InsertMenuItemW(hMenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
943 _InsertMenuItemW(hMenu, indexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
944 }
945
946 return iIdCmdLast;
947 }
948
949 static
950 HRESULT
951 WINAPI
952 IDefaultContextMenu_fnQueryContextMenu(
953 IContextMenu2 *iface,
954 HMENU hmenu,
955 UINT indexMenu,
956 UINT idCmdFirst,
957 UINT idCmdLast,
958 UINT uFlags)
959 {
960 IDefaultContextMenuImpl * This = impl_from_IContextMenu(iface);
961 if (This->dcm.cidl)
962 {
963 idCmdFirst = BuildShellItemContextMenu(This, hmenu, idCmdFirst, idCmdLast, uFlags);
964 }
965 else
966 {
967 idCmdFirst = BuildBackgroundContextMenu(This, hmenu, idCmdFirst, idCmdLast, uFlags);
968 }
969
970 return S_OK;
971 }
972
973 static
974 HRESULT
975 NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh)
976 {
977 LPSHELLBROWSER lpSB;
978 LPSHELLVIEW lpSV = NULL;
979 HWND hwndSV = NULL;
980
981 if((lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0)))
982 {
983 if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
984 {
985 IShellView_GetWindow(lpSV, &hwndSV);
986 }
987 }
988
989 if (LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_REFRESH || bRefresh)
990 {
991 if (lpSV)
992 IShellView_Refresh(lpSV);
993
994 return S_OK;
995 }
996
997 SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0), 0);
998
999 return S_OK;
1000 }
1001
1002 static
1003 HRESULT
1004 DoPaste(
1005 IDefaultContextMenuImpl *This,
1006 LPCMINVOKECOMMANDINFO lpcmi)
1007 {
1008 IDataObject * pda;
1009 STGMEDIUM medium;
1010 FORMATETC formatetc;
1011 LPITEMIDLIST * apidl;
1012 LPITEMIDLIST pidl;
1013 IShellFolder *psfFrom = NULL, *psfDesktop, *psfTarget = NULL;
1014 LPIDA lpcida;
1015 ISFHelper *psfhlpdst, *psfhlpsrc;
1016 HRESULT hr;
1017
1018 if (OleGetClipboard(&pda) != S_OK)
1019 return E_FAIL;
1020
1021 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1022 hr = IDataObject_GetData(pda,&formatetc,&medium);
1023
1024 if (FAILED(hr))
1025 {
1026 IDataObject_Release(pda);
1027 return E_FAIL;
1028 }
1029
1030 /* lock the handle */
1031 lpcida = GlobalLock(medium.u.hGlobal);
1032 if (!lpcida)
1033 {
1034 ReleaseStgMedium(&medium);
1035 IDataObject_Release(pda);
1036 return E_FAIL;
1037 }
1038
1039 /* convert the data into pidl */
1040 apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1041
1042 if (!apidl)
1043 return E_FAIL;
1044
1045 if (FAILED(SHGetDesktopFolder(&psfDesktop)))
1046 {
1047 SHFree(pidl);
1048 _ILFreeaPidl(apidl, lpcida->cidl);
1049 ReleaseStgMedium(&medium);
1050 IDataObject_Release(pda);
1051 return E_FAIL;
1052 }
1053
1054 if (_ILIsDesktop(pidl))
1055 {
1056 /* use desktop shellfolder */
1057 psfFrom = psfDesktop;
1058 }
1059 else if (FAILED(IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (LPVOID*)&psfFrom)))
1060 {
1061 ERR("no IShellFolder\n");
1062
1063 IShellFolder_Release(psfDesktop);
1064 SHFree(pidl);
1065 _ILFreeaPidl(apidl, lpcida->cidl);
1066 ReleaseStgMedium(&medium);
1067 IDataObject_Release(pda);
1068
1069 return E_FAIL;
1070 }
1071
1072 if (This->dcm.cidl)
1073 {
1074 IShellFolder_Release(psfDesktop);
1075 hr = IShellFolder_BindToObject(This->dcm.psf, This->dcm.apidl[0], NULL, &IID_IShellFolder, (LPVOID*)&psfTarget);
1076 }
1077 else
1078 {
1079 IPersistFolder2 *ppf2 = NULL;
1080 LPITEMIDLIST pidl;
1081
1082 /* cidl is zero due to explorer view */
1083 hr = IShellFolder_QueryInterface (This->dcm.psf, &IID_IPersistFolder2, (LPVOID *) &ppf2);
1084 if (SUCCEEDED(hr))
1085 {
1086 hr = IPersistFolder2_GetCurFolder (ppf2, &pidl);
1087 IPersistFolder2_Release(ppf2);
1088 if (SUCCEEDED(hr))
1089 {
1090 if (_ILIsDesktop(pidl))
1091 {
1092 /* use desktop shellfolder */
1093 psfTarget = psfDesktop;
1094 }
1095 else
1096 {
1097 /* retrieve target desktop folder */
1098 hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (LPVOID*)&psfTarget);
1099 }
1100 TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget, _ILIsDesktop(pidl));
1101 ILFree(pidl);
1102 }
1103 }
1104 }
1105
1106 if (FAILED(hr))
1107 {
1108 ERR("no IShellFolder\n");
1109
1110 IShellFolder_Release(psfFrom);
1111 SHFree(pidl);
1112 _ILFreeaPidl(apidl, lpcida->cidl);
1113 ReleaseStgMedium(&medium);
1114 IDataObject_Release(pda);
1115
1116 return E_FAIL;
1117 }
1118
1119
1120 /* get source and destination shellfolder */
1121 if (FAILED(IShellFolder_QueryInterface(psfTarget, &IID_ISFHelper, (LPVOID*)&psfhlpdst)))
1122 {
1123 ERR("no IID_ISFHelper for destination\n");
1124
1125 IShellFolder_Release(psfFrom);
1126 IShellFolder_Release(psfTarget);
1127 SHFree(pidl);
1128 _ILFreeaPidl(apidl, lpcida->cidl);
1129 ReleaseStgMedium(&medium);
1130 IDataObject_Release(pda);
1131
1132 return E_FAIL;
1133 }
1134
1135 if (FAILED(IShellFolder_QueryInterface(psfFrom, &IID_ISFHelper, (LPVOID*)&psfhlpsrc)))
1136 {
1137 ERR("no IID_ISFHelper for source\n");
1138
1139 ISFHelper_Release(psfhlpdst);
1140 IShellFolder_Release(psfFrom);
1141 IShellFolder_Release(psfTarget);
1142 SHFree(pidl);
1143 _ILFreeaPidl(apidl, lpcida->cidl);
1144 ReleaseStgMedium(&medium);
1145 IDataObject_Release(pda);
1146 return E_FAIL;
1147 }
1148
1149 /* FIXXME
1150 * do we want to perform a copy or move ???
1151 */
1152 hr = ISFHelper_CopyItems(psfhlpdst, psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl);
1153
1154 ISFHelper_Release(psfhlpdst);
1155 ISFHelper_Release(psfhlpsrc);
1156 IShellFolder_Release(psfFrom);
1157 IShellFolder_Release(psfTarget);
1158 SHFree(pidl);
1159 _ILFreeaPidl(apidl, lpcida->cidl);
1160 ReleaseStgMedium(&medium);
1161 IDataObject_Release(pda);
1162 TRACE("CP result %x\n",hr);
1163 return S_OK;
1164 }
1165
1166 static
1167 HRESULT
1168 DoOpenOrExplore(
1169 IDefaultContextMenuImpl *iface,
1170 LPCMINVOKECOMMANDINFO lpcmi)
1171 {
1172
1173
1174 return E_FAIL;
1175 }
1176
1177 BOOL
1178 GetUniqueFileName(LPWSTR szBasePath, LPWSTR szExt, LPWSTR szTarget, BOOL bShortcut)
1179 {
1180 UINT RetryCount = 0, Length;
1181 WCHAR szLnk[40];
1182 HANDLE hFile;
1183
1184 if (!bShortcut)
1185 {
1186 Length = LoadStringW(shell32_hInstance, IDS_LNK_FILE, szLnk, sizeof(szLnk)/sizeof(WCHAR));
1187 }
1188
1189 do
1190 {
1191 if (!bShortcut)
1192 {
1193 if (RetryCount)
1194 swprintf(szTarget, L"%s%s(%u).%s", szLnk, szBasePath, RetryCount, szExt);
1195 else
1196 swprintf(szTarget, L"%s%s.%s", szLnk, szBasePath, szExt);
1197 }
1198 else
1199 {
1200 if (RetryCount)
1201 swprintf(szTarget, L"%s(%u).%s", szBasePath, RetryCount, szExt);
1202 else
1203 swprintf(szTarget, L"%s.%s", szBasePath, szExt);
1204 }
1205
1206 hFile = CreateFileW(szTarget, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
1207 if (hFile != INVALID_HANDLE_VALUE)
1208 {
1209 CloseHandle(hFile);
1210 return TRUE;
1211 }
1212
1213 }while(RetryCount++ < 100);
1214
1215 return FALSE;
1216
1217 }
1218
1219 static
1220 HRESULT
1221 DoCreateLink(
1222 IDefaultContextMenuImpl *This,
1223 LPCMINVOKECOMMANDINFO lpcmi)
1224 {
1225 WCHAR szPath[MAX_PATH];
1226 WCHAR szTarget[MAX_PATH] = {0};
1227 WCHAR szDirPath[MAX_PATH];
1228 LPWSTR pszFile;
1229 STRRET strFile;
1230 LPWSTR pszExt;
1231 HRESULT hr;
1232 IShellLinkW * nLink;
1233 IPersistFile * ipf;
1234 static WCHAR szLnk[] = L"lnk";
1235
1236 if (IShellFolder2_GetDisplayNameOf(This->dcm.psf, This->dcm.apidl[0], SHGDN_FORPARSING, &strFile) != S_OK)
1237 {
1238 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1239 return E_FAIL;
1240 }
1241
1242 if (StrRetToBufW(&strFile, This->dcm.apidl[0], szPath, MAX_PATH) != S_OK)
1243 return E_FAIL;
1244
1245 pszExt = wcsrchr(szPath, L'.');
1246
1247 if (pszExt && !wcsicmp(pszExt + 1, szLnk))
1248 {
1249 if (!GetUniqueFileName(szPath, pszExt + 1, szTarget, TRUE))
1250 return E_FAIL;
1251
1252 hr = IShellLink_ConstructFromFile(NULL, &IID_IPersistFile, This->dcm.apidl[0], (LPVOID*)&ipf);
1253 if (hr != S_OK)
1254 {
1255 return hr;
1256 }
1257 hr = IPersistFile_Save(ipf, szTarget, FALSE);
1258 IPersistFile_Release(ipf);
1259 NotifyShellViewWindow(lpcmi, TRUE);
1260 return hr;
1261 }
1262 else
1263 {
1264 if (!GetUniqueFileName(szPath, szLnk, szTarget, TRUE))
1265 return E_FAIL;
1266
1267 hr = IShellLink_Constructor(NULL, &IID_IShellLinkW, (LPVOID*)&nLink);
1268 if (hr != S_OK)
1269 {
1270 return E_FAIL;
1271 }
1272
1273 GetFullPathName(szPath, MAX_PATH, szDirPath, &pszFile);
1274 if (pszFile) pszFile[0] = 0;
1275
1276 if (SUCCEEDED(IShellLinkW_SetPath(nLink, szPath)) &&
1277 SUCCEEDED(IShellLinkW_SetWorkingDirectory(nLink, szDirPath)))
1278 {
1279 if (SUCCEEDED(IShellLinkW_QueryInterface(nLink, &IID_IPersistFile, (LPVOID*)&ipf)))
1280 {
1281 hr = IPersistFile_Save(ipf, szTarget, TRUE);
1282 IPersistFile_Release(ipf);
1283 }
1284 }
1285 IShellLinkW_Release(nLink);
1286 NotifyShellViewWindow(lpcmi, TRUE);
1287 return hr;
1288 }
1289 }
1290
1291 static
1292 HRESULT
1293 DoDelete(
1294 IDefaultContextMenuImpl *This,
1295 LPCMINVOKECOMMANDINFO lpcmi)
1296 {
1297 HRESULT hr;
1298 STRRET strTemp;
1299 WCHAR szPath[MAX_PATH];
1300 SHFILEOPSTRUCTW op;
1301 int ret;
1302 LPSHELLBROWSER lpSB;
1303 HWND hwnd;
1304
1305
1306 hr = IShellFolder2_GetDisplayNameOf(This->dcm.psf, This->dcm.apidl[0], SHGDN_FORPARSING, &strTemp);
1307 if(hr != S_OK)
1308 {
1309 ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
1310 return hr;
1311 }
1312 ZeroMemory(szPath, sizeof(szPath));
1313 hr = StrRetToBufW(&strTemp, This->dcm.apidl[0], szPath, MAX_PATH);
1314 if (hr != S_OK)
1315 {
1316 ERR("StrRetToBufW failed with %x\n", hr);
1317 return hr;
1318 }
1319 /* FIXME
1320 * implement deletion with multiple files
1321 */
1322
1323 ZeroMemory(&op, sizeof(op));
1324 op.hwnd = GetActiveWindow();
1325 op.wFunc = FO_DELETE;
1326 op.pFrom = szPath;
1327 op.fFlags = FOF_ALLOWUNDO;
1328 ret = SHFileOperationW(&op);
1329
1330 if (ret)
1331 {
1332 TRACE("SHFileOperation failed with %0x%x", GetLastError());
1333 return S_OK;
1334 }
1335
1336 /* get the active IShellView */
1337 if ((lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0)))
1338 {
1339 /* is the treeview focused */
1340 if (SUCCEEDED(IShellBrowser_GetControlWindow(lpSB, FCW_TREE, &hwnd)))
1341 {
1342 HTREEITEM hItem = TreeView_GetSelection(hwnd);
1343 if (hItem)
1344 {
1345 (void)TreeView_DeleteItem(hwnd, hItem);
1346 }
1347 }
1348 }
1349 NotifyShellViewWindow(lpcmi, TRUE);
1350
1351 return S_OK;
1352
1353 }
1354
1355 static
1356 HRESULT
1357 DoCopyOrCut(
1358 IDefaultContextMenuImpl *iface,
1359 LPCMINVOKECOMMANDINFO lpcmi,
1360 BOOL bCopy)
1361 {
1362 LPSHELLBROWSER lpSB;
1363 LPSHELLVIEW lpSV;
1364 LPDATAOBJECT pDataObj;
1365 HRESULT hr;
1366
1367 if (SUCCEEDED(SHCreateDataObject(iface->dcm.pidlFolder, iface->dcm.cidl, iface->dcm.apidl, NULL, &IID_IDataObject, (void**)&pDataObj)))
1368 {
1369 hr = OleSetClipboard(pDataObj);
1370 IDataObject_Release(pDataObj);
1371 return hr;
1372 }
1373
1374 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0);
1375 if (!lpSB)
1376 {
1377 TRACE("failed to get shellbrowser\n");
1378 return E_FAIL;
1379 }
1380
1381 hr = IShellBrowser_QueryActiveShellView(lpSB, &lpSV);
1382 if (FAILED(hr))
1383 {
1384 TRACE("failed to query the active shellview\n");
1385 return hr;
1386 }
1387
1388 hr = IShellView_GetItemObject(lpSV, SVGIO_SELECTION, &IID_IDataObject, (LPVOID*)&pDataObj);
1389 if (FAILED(hr))
1390 {
1391 TRACE("failed to get item object\n");
1392 return hr;
1393 }
1394
1395 hr = OleSetClipboard(pDataObj);
1396 if (FAILED(hr))
1397 {
1398 WARN("OleSetClipboard failed");
1399 }
1400 IDataObject_Release(pDataObj);
1401 IShellView_Release(lpSV);
1402 return S_OK;
1403 }
1404
1405 static
1406 HRESULT
1407 DoRename(
1408 IDefaultContextMenuImpl *This,
1409 LPCMINVOKECOMMANDINFO lpcmi)
1410 {
1411 LPSHELLBROWSER lpSB;
1412 LPSHELLVIEW lpSV;
1413 HWND hwnd;
1414
1415 /* get the active IShellView */
1416 if ((lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0)))
1417 {
1418 /* is the treeview focused */
1419 if (SUCCEEDED(IShellBrowser_GetControlWindow(lpSB, FCW_TREE, &hwnd)))
1420 {
1421 HTREEITEM hItem = TreeView_GetSelection(hwnd);
1422 if (hItem)
1423 {
1424 (void)TreeView_EditLabel(hwnd, hItem);
1425 }
1426 }
1427
1428 if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
1429 {
1430 IShellView_SelectItem(lpSV, This->dcm.apidl[0],
1431 SVSI_DESELECTOTHERS|SVSI_EDIT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_SELECT);
1432 IShellView_Release(lpSV);
1433 return S_OK;
1434 }
1435 }
1436 return E_FAIL;
1437 }
1438
1439 static
1440 HRESULT
1441 DoProperties(
1442 IDefaultContextMenuImpl *This,
1443 LPCMINVOKECOMMANDINFO lpcmi)
1444 {
1445 WCHAR szDrive[MAX_PATH];
1446 STRRET strFile;
1447
1448 if (This->dcm.cidl &&_ILIsMyComputer(This->dcm.apidl[0]))
1449 {
1450 ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL);
1451 return S_OK;
1452 }
1453 else if (This->dcm.cidl == 0 && _ILIsDesktop(This->dcm.pidlFolder))
1454 {
1455 ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL);
1456 return S_OK;
1457 }
1458 else if (_ILIsDrive(This->dcm.apidl[0]))
1459 {
1460 ILGetDisplayName(This->dcm.apidl[0], szDrive);
1461 SH_ShowDriveProperties(szDrive, This->dcm.pidlFolder, This->dcm.apidl);
1462 return S_OK;
1463 }
1464 else if (_ILIsNetHood(This->dcm.apidl[0]))
1465 {
1466 //FIXME path!
1467 ShellExecuteW(NULL, L"open", L"explorer.exe",
1468 L"/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
1469 NULL, SW_SHOWDEFAULT);
1470 return S_OK;
1471 }
1472 else if (_ILIsBitBucket(This->dcm.apidl[0]))
1473 {
1474 /* FIXME
1475 * detect the drive path of bitbucket if appropiate
1476 */
1477
1478 SH_ShowRecycleBinProperties(L'C');
1479 return S_OK;
1480 }
1481
1482 if (This->dcm.cidl > 1)
1483 WARN("SHMultiFileProperties is not yet implemented\n");
1484
1485 if (IShellFolder2_GetDisplayNameOf(This->dcm.psf, This->dcm.apidl[0], SHGDN_FORPARSING, &strFile) != S_OK)
1486 {
1487 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1488 return E_FAIL;
1489 }
1490
1491 if (StrRetToBufW(&strFile, This->dcm.apidl[0], szDrive, MAX_PATH) != S_OK)
1492 return E_FAIL;
1493
1494 return SH_ShowPropertiesDialog(szDrive, This->dcm.pidlFolder, This->dcm.apidl);
1495 }
1496
1497 static
1498 HRESULT
1499 DoFormat(
1500 IDefaultContextMenuImpl *This,
1501 LPCMINVOKECOMMANDINFO lpcmi)
1502 {
1503 char sDrive[5] = {0};
1504
1505 if (!_ILGetDrive(This->dcm.apidl[0], sDrive, sizeof(sDrive)))
1506 {
1507 ERR("pidl is not a drive\n");
1508 return E_FAIL;
1509 }
1510
1511 SHFormatDrive(lpcmi->hwnd, sDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
1512 return S_OK;
1513 }
1514
1515 static
1516 HRESULT
1517 DoDynamicShellExtensions(
1518 IDefaultContextMenuImpl *This,
1519 LPCMINVOKECOMMANDINFO lpcmi)
1520 {
1521 UINT verb = LOWORD(lpcmi->lpVerb);
1522 PDynamicShellEntry pCurrent = This->dhead;
1523
1524 TRACE("verb %p first %x last %x", lpcmi->lpVerb, This->iIdSHEFirst, This->iIdSHELast);
1525
1526 while(pCurrent && verb > pCurrent->iIdCmdFirst + pCurrent->NumIds)
1527 pCurrent = pCurrent->Next;
1528
1529 if (!pCurrent)
1530 return E_FAIL;
1531
1532 if (verb >= pCurrent->iIdCmdFirst && verb <= pCurrent->iIdCmdFirst + pCurrent->NumIds)
1533 {
1534 /* invoke the dynamic context menu */
1535 lpcmi->lpVerb = MAKEINTRESOURCEA(verb - pCurrent->iIdCmdFirst);
1536 return IContextMenu_InvokeCommand(pCurrent->CMenu, lpcmi);
1537 }
1538
1539 return E_FAIL;
1540 }
1541
1542
1543 static
1544 HRESULT
1545 DoStaticShellExtensions(
1546 IDefaultContextMenuImpl *This,
1547 LPCMINVOKECOMMANDINFO lpcmi)
1548 {
1549 STRRET strFile;
1550 WCHAR szPath[MAX_PATH];
1551 WCHAR szDir[MAX_PATH];
1552 SHELLEXECUTEINFOW sei;
1553 PStaticShellEntry pCurrent = This->shead;
1554 int verb = LOWORD(lpcmi->lpVerb) - This->iIdSCMFirst;
1555
1556
1557 while(pCurrent && verb-- > 0)
1558 pCurrent = pCurrent->Next;
1559
1560 if (verb > 0)
1561 return E_FAIL;
1562
1563
1564 if (IShellFolder2_GetDisplayNameOf(This->dcm.psf, This->dcm.apidl[0], SHGDN_FORPARSING, &strFile) != S_OK)
1565 {
1566 ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
1567 return E_FAIL;
1568 }
1569
1570 if (StrRetToBufW(&strFile, This->dcm.apidl[0], szPath, MAX_PATH) != S_OK)
1571 return E_FAIL;
1572
1573 wcscpy(szDir, szPath);
1574 PathRemoveFileSpec(szDir);
1575
1576 ZeroMemory(&sei, sizeof(sei));
1577 sei.cbSize = sizeof(sei);
1578 sei.fMask = SEE_MASK_CLASSNAME;
1579 sei.lpClass = pCurrent->szClass;
1580 sei.hwnd = lpcmi->hwnd;
1581 sei.nShow = SW_SHOWNORMAL;
1582 sei.lpVerb = pCurrent->szVerb;
1583 sei.lpFile = szPath;
1584 sei.lpDirectory = szDir;
1585 ShellExecuteExW(&sei);
1586 return S_OK;
1587
1588 }
1589
1590 static
1591 HRESULT
1592 WINAPI
1593 IDefaultContextMenu_fnInvokeCommand(
1594 IContextMenu2 *iface,
1595 LPCMINVOKECOMMANDINFO lpcmi)
1596 {
1597 IDefaultContextMenuImpl * This = impl_from_IContextMenu(iface);
1598
1599 switch(LOWORD(lpcmi->lpVerb))
1600 {
1601 case FCIDM_SHVIEW_BIGICON:
1602 case FCIDM_SHVIEW_SMALLICON:
1603 case FCIDM_SHVIEW_LISTVIEW:
1604 case FCIDM_SHVIEW_REPORTVIEW:
1605 case 0x30: /* FIX IDS in resource files */
1606 case 0x31:
1607 case 0x32:
1608 case 0x33:
1609 case FCIDM_SHVIEW_AUTOARRANGE:
1610 case FCIDM_SHVIEW_SNAPTOGRID:
1611 case FCIDM_SHVIEW_REFRESH:
1612 return NotifyShellViewWindow(lpcmi, FALSE);
1613 case FCIDM_SHVIEW_INSERT:
1614 case FCIDM_SHVIEW_INSERTLINK:
1615 return DoPaste(This, lpcmi);
1616 case FCIDM_SHVIEW_OPEN:
1617 case FCIDM_SHVIEW_EXPLORE:
1618 return DoOpenOrExplore(This, lpcmi);
1619 case FCIDM_SHVIEW_COPY:
1620 case FCIDM_SHVIEW_CUT:
1621 return DoCopyOrCut(This, lpcmi, LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_COPY);
1622 case FCIDM_SHVIEW_CREATELINK:
1623 return DoCreateLink(This, lpcmi);
1624 case FCIDM_SHVIEW_DELETE:
1625 return DoDelete(This, lpcmi);
1626 case FCIDM_SHVIEW_RENAME:
1627 return DoRename(This, lpcmi);
1628 case FCIDM_SHVIEW_PROPERTIES:
1629 return DoProperties(This, lpcmi);
1630 case 0x7ABC:
1631 return DoFormat(This, lpcmi);
1632 }
1633
1634 if (This->iIdSHEFirst && This->iIdSHELast)
1635 {
1636 if (LOWORD(lpcmi->lpVerb) >= This->iIdSHEFirst && LOWORD(lpcmi->lpVerb) <= This->iIdSHELast)
1637 {
1638 return DoDynamicShellExtensions(This, lpcmi);
1639 }
1640 }
1641
1642 if (This->iIdSCMFirst && This->iIdSCMLast)
1643 {
1644 if (LOWORD(lpcmi->lpVerb) >= This->iIdSCMFirst && LOWORD(lpcmi->lpVerb) <= This->iIdSCMLast)
1645 {
1646 return DoStaticShellExtensions(This, lpcmi);
1647 }
1648 }
1649
1650 FIXME("Unhandled Verb %xl\n",LOWORD(lpcmi->lpVerb));
1651 return E_UNEXPECTED;
1652 }
1653
1654 static
1655 HRESULT
1656 WINAPI
1657 IDefaultContextMenu_fnGetCommandString(
1658 IContextMenu2 *iface,
1659 UINT_PTR idCommand,
1660 UINT uFlags,
1661 UINT* lpReserved,
1662 LPSTR lpszName,
1663 UINT uMaxNameLen)
1664 {
1665
1666 return S_OK;
1667 }
1668
1669 static
1670 HRESULT
1671 WINAPI
1672 IDefaultContextMenu_fnHandleMenuMsg(
1673 IContextMenu2 *iface,
1674 UINT uMsg,
1675 WPARAM wParam,
1676 LPARAM lParam)
1677 {
1678
1679 return S_OK;
1680 }
1681
1682 static const IContextMenu2Vtbl cmvt =
1683 {
1684 IDefaultContextMenu_fnQueryInterface,
1685 IDefaultContextMenu_fnAddRef,
1686 IDefaultContextMenu_fnRelease,
1687 IDefaultContextMenu_fnQueryContextMenu,
1688 IDefaultContextMenu_fnInvokeCommand,
1689 IDefaultContextMenu_fnGetCommandString,
1690 IDefaultContextMenu_fnHandleMenuMsg
1691 };
1692 static
1693 HRESULT
1694 IDefaultContextMenu_Constructor(
1695 const DEFCONTEXTMENU *pdcm,
1696 REFIID riid,
1697 void **ppv)
1698 {
1699 IDefaultContextMenuImpl * This;
1700 HRESULT hr = E_FAIL;
1701 IDataObject * pDataObj;
1702
1703 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDefaultContextMenuImpl));
1704 if (This)
1705 {
1706 This->lpVtbl = &cmvt;
1707 This->ref = 1;
1708 TRACE("cidl %u\n", This->dcm.cidl);
1709 if (SUCCEEDED(SHCreateDataObject(pdcm->pidlFolder, pdcm->cidl, pdcm->apidl, NULL, &IID_IDataObject, (void**)&pDataObj)))
1710 {
1711 This->pDataObj = pDataObj;
1712 }
1713 CopyMemory(&This->dcm, pdcm, sizeof(DEFCONTEXTMENU));
1714 hr = IDefaultContextMenu_fnQueryInterface((IContextMenu2*)This, riid, ppv);
1715 if (SUCCEEDED(hr))
1716 IContextMenu_Release((IContextMenu2*)This);
1717 }
1718
1719 TRACE("This(%p)(%x) cidl %u\n",This, hr, This->dcm.cidl);
1720 return hr;
1721 }
1722
1723 /*************************************************************************
1724 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1725 *
1726 */
1727
1728 HRESULT
1729 WINAPI
1730 SHCreateDefaultContextMenu(
1731 const DEFCONTEXTMENU *pdcm,
1732 REFIID riid,
1733 void **ppv)
1734 {
1735 HRESULT hr = E_FAIL;
1736
1737 *ppv = NULL;
1738 hr = IDefaultContextMenu_Constructor( pdcm, riid, ppv );
1739
1740 TRACE("pcm %p hr %x\n", pdcm, hr);
1741 return hr;
1742 }
1743
1744 /*************************************************************************
1745 * CDefFolderMenu_Create2 [SHELL32.701]
1746 *
1747 */
1748
1749 HRESULT
1750 WINAPI
1751 CDefFolderMenu_Create2(
1752 LPCITEMIDLIST pidlFolder,
1753 HWND hwnd,
1754 UINT cidl,
1755 LPCITEMIDLIST *apidl,
1756 IShellFolder *psf,
1757 LPFNDFMCALLBACK lpfn,
1758 UINT nKeys,
1759 const HKEY *ahkeyClsKeys,
1760 IContextMenu **ppcm)
1761 {
1762 DEFCONTEXTMENU pdcm;
1763 HRESULT hr;
1764
1765 pdcm.hwnd = hwnd;
1766 pdcm.pcmcb = NULL;
1767 pdcm.pidlFolder = pidlFolder;
1768 pdcm.psf = psf;
1769 pdcm.cidl = cidl;
1770 pdcm.apidl = apidl;
1771 pdcm.punkAssociationInfo = NULL;
1772 pdcm.cKeys = nKeys;
1773 pdcm.aKeys = ahkeyClsKeys;
1774
1775 hr = SHCreateDefaultContextMenu(&pdcm, &IID_IContextMenu, (void**)ppcm);
1776 return hr;
1777 }
1778