d3d9: Don't expose wined3d internal flags to the application.
[wine] / dlls / shell32 / shlview_cmenu.c
1 /*
2  *      IContextMenu for items in the shellview
3  *
4  * Copyright 1998-2000 Juergen Schmied <juergen.schmied@debitel.net>,
5  *                                     <juergen.schmied@metronet.de>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <string.h>
23
24 #define COBJMACROS
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27
28 #include "winerror.h"
29 #include "wine/debug.h"
30
31 #include "windef.h"
32 #include "wingdi.h"
33 #include "pidl.h"
34 #include "undocshell.h"
35 #include "shlobj.h"
36 #include "winreg.h"
37 #include "prsht.h"
38
39 #include "shell32_main.h"
40 #include "shellfolder.h"
41
42 #include "shresdef.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45
46 typedef struct
47 {
48     IContextMenu2 IContextMenu2_iface;
49     LONG ref;
50     IShellFolder* parent;
51     UINT verb_offset;
52 } ContextMenu;
53
54 typedef struct
55 {
56     ContextMenu     menu;
57     LPITEMIDLIST    pidl;       /* root pidl */
58     LPITEMIDLIST    *apidl;     /* array of child pidls */
59     UINT            cidl;
60     BOOL            bAllValues;
61 } ItemMenu;
62
63 static inline ItemMenu *impl_from_IContextMenu2_Item(IContextMenu2 *iface)
64 {
65     return CONTAINING_RECORD(iface, ItemMenu, menu.IContextMenu2_iface);
66 }
67
68 typedef struct
69 {
70     ContextMenu menu;
71     BOOL desktop;
72 } BackgroundMenu;
73
74 static inline BackgroundMenu *impl_from_IContextMenu2_Back(IContextMenu2 *iface)
75 {
76     return CONTAINING_RECORD(iface, BackgroundMenu, menu.IContextMenu2_iface);
77 }
78
79 static BOOL ItemMenu_CanRenameItems(ItemMenu *This)
80 {
81     DWORD attr;
82
83     TRACE("(%p)\n", This);
84
85     /* can't rename more than one item at a time*/
86     if (!This->apidl || This->cidl > 1) return FALSE;
87
88     attr = SFGAO_CANRENAME;
89     IShellFolder_GetAttributesOf(This->menu.parent, 1, (LPCITEMIDLIST*)This->apidl, &attr);
90     return attr & SFGAO_CANRENAME;
91 }
92
93 static HRESULT WINAPI ItemMenu_QueryInterface(IContextMenu2 *iface, REFIID riid, LPVOID *ppvObj)
94 {
95         ItemMenu *This = impl_from_IContextMenu2_Item(iface);
96
97         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
98
99         *ppvObj = NULL;
100
101         if(IsEqualIID(riid, &IID_IUnknown) ||
102            IsEqualIID(riid, &IID_IContextMenu) ||
103            IsEqualIID(riid, &IID_IContextMenu2))
104         {
105           *ppvObj = This;
106         }
107         else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
108         {
109           FIXME("-- LPSHELLEXTINIT pointer requested\n");
110         }
111
112         if(*ppvObj)
113         {
114           IUnknown_AddRef((IUnknown*)*ppvObj);
115           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
116           return S_OK;
117         }
118         TRACE("-- Interface: E_NOINTERFACE\n");
119         return E_NOINTERFACE;
120 }
121
122 static ULONG WINAPI ItemMenu_AddRef(IContextMenu2 *iface)
123 {
124         ItemMenu *This = impl_from_IContextMenu2_Item(iface);
125         ULONG refCount = InterlockedIncrement(&This->menu.ref);
126
127         TRACE("(%p)->(count=%u)\n", This, refCount - 1);
128
129         return refCount;
130 }
131
132 static ULONG WINAPI ItemMenu_Release(IContextMenu2 *iface)
133 {
134         ItemMenu *This = impl_from_IContextMenu2_Item(iface);
135         ULONG refCount = InterlockedDecrement(&This->menu.ref);
136
137         TRACE("(%p)->(count=%i)\n", This, refCount + 1);
138
139         if (!refCount)
140         {
141           TRACE(" destroying IContextMenu(%p)\n",This);
142
143           if(This->menu.parent)
144             IShellFolder_Release(This->menu.parent);
145
146           SHFree(This->pidl);
147
148           /*make sure the pidl is freed*/
149           _ILFreeaPidl(This->apidl, This->cidl);
150
151           HeapFree(GetProcessHeap(),0,This);
152         }
153         return refCount;
154 }
155
156 static HRESULT WINAPI ItemMenu_QueryContextMenu(
157         IContextMenu2 *iface,
158         HMENU hmenu,
159         UINT indexMenu,
160         UINT idCmdFirst,
161         UINT idCmdLast,
162         UINT uFlags)
163 {
164     ItemMenu *This = impl_from_IContextMenu2_Item(iface);
165     INT uIDMax;
166
167     TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
168
169     This->menu.verb_offset=idCmdFirst;
170
171     if(!(CMF_DEFAULTONLY & uFlags) && This->cidl>0)
172     {
173         HMENU hmenures = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_SHV_FILE));
174
175         if(uFlags & CMF_EXPLORE)
176             RemoveMenu(hmenures, FCIDM_SHVIEW_OPEN, MF_BYCOMMAND);
177
178         uIDMax = Shell_MergeMenus(hmenu, GetSubMenu(hmenures, 0), indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS);
179
180         DestroyMenu(hmenures);
181
182         if(This->bAllValues)
183         {
184             MENUITEMINFOW mi;
185             WCHAR str[255];
186             mi.cbSize = sizeof(mi);
187             mi.fMask = MIIM_ID | MIIM_STRING | MIIM_FTYPE;
188             mi.dwTypeData = str;
189             mi.cch = 255;
190             GetMenuItemInfoW(hmenu, FCIDM_SHVIEW_EXPLORE, MF_BYCOMMAND, &mi);
191             RemoveMenu(hmenu, FCIDM_SHVIEW_EXPLORE, MF_BYCOMMAND);
192
193             mi.cbSize = sizeof(mi);
194             mi.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
195             mi.dwTypeData = str;
196             mi.fState = MFS_ENABLED;
197             mi.wID = FCIDM_SHVIEW_EXPLORE;
198             mi.fType = MFT_STRING;
199             InsertMenuItemW(hmenu, (uFlags & CMF_EXPLORE) ? 1 : 2, MF_BYPOSITION, &mi);
200         }
201
202         SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
203
204         if(uFlags & ~CMF_CANRENAME)
205             RemoveMenu(hmenu, FCIDM_SHVIEW_RENAME, MF_BYCOMMAND);
206         else
207             EnableMenuItem(hmenu, FCIDM_SHVIEW_RENAME, MF_BYCOMMAND | ItemMenu_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED);
208
209         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, uIDMax-idCmdFirst);
210     }
211     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
212 }
213
214 /**************************************************************************
215 * DoOpenExplore
216 *
217 *  for folders only
218 */
219
220 static void DoOpenExplore(ItemMenu *This, HWND hwnd, LPCSTR verb)
221 {
222         UINT i, bFolderFound = FALSE;
223         LPITEMIDLIST    pidlFQ;
224         SHELLEXECUTEINFOA       sei;
225
226         /* Find the first item in the list that is not a value. These commands
227             should never be invoked if there isn't at least one folder item in the list.*/
228
229         for(i = 0; i<This->cidl; i++)
230         {
231           if(!_ILIsValue(This->apidl[i]))
232           {
233             bFolderFound = TRUE;
234             break;
235           }
236         }
237
238         if (!bFolderFound) return;
239
240         pidlFQ = ILCombine(This->pidl, This->apidl[i]);
241
242         ZeroMemory(&sei, sizeof(sei));
243         sei.cbSize = sizeof(sei);
244         sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
245         sei.lpIDList = pidlFQ;
246         sei.lpClass = "Folder";
247         sei.hwnd = hwnd;
248         sei.nShow = SW_SHOWNORMAL;
249         sei.lpVerb = verb;
250         ShellExecuteExA(&sei);
251         SHFree(pidlFQ);
252 }
253
254 /**************************************************************************
255  * DoDelete
256  *
257  * deletes the currently selected items
258  */
259 static void DoDelete(ItemMenu *This)
260 {
261         ISFHelper * psfhlp;
262
263         IShellFolder_QueryInterface(This->menu.parent, &IID_ISFHelper, (LPVOID*)&psfhlp);
264         if (psfhlp)
265         {
266           ISFHelper_DeleteItems(psfhlp, This->cidl, (LPCITEMIDLIST *)This->apidl);
267           ISFHelper_Release(psfhlp);
268         }
269 }
270
271 /**************************************************************************
272  * DoCopyOrCut
273  *
274  * copies the currently selected items into the clipboard
275  */
276 static BOOL DoCopyOrCut(ItemMenu *This, HWND hwnd, BOOL bCut)
277 {
278         LPSHELLBROWSER  lpSB;
279         LPSHELLVIEW     lpSV;
280         LPDATAOBJECT    lpDo;
281
282         TRACE("(%p)->(wnd=%p,bCut=0x%08x)\n",This, hwnd, bCut);
283
284         /* get the active IShellView */
285         if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
286         {
287           if (SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
288           {
289             if (SUCCEEDED(IShellView_GetItemObject(lpSV, SVGIO_SELECTION, &IID_IDataObject, (LPVOID*)&lpDo)))
290             {
291               OleSetClipboard(lpDo);
292               IDataObject_Release(lpDo);
293             }
294             IShellView_Release(lpSV);
295           }
296         }
297         return TRUE;
298 }
299
300 /**************************************************************************
301  * Properties_AddPropSheetCallback
302  *
303  * Used by DoOpenProperties through SHCreatePropSheetExtArrayEx to add
304  * propertysheet pages from shell extensions.
305  */
306 static BOOL CALLBACK Properties_AddPropSheetCallback(HPROPSHEETPAGE hpage, LPARAM lparam)
307 {
308         LPPROPSHEETHEADERW psh = (LPPROPSHEETHEADERW) lparam;
309         psh->u3.phpage[psh->nPages++] = hpage;
310
311         return TRUE;
312 }
313
314 /**************************************************************************
315  * DoOpenProperties
316  */
317 static void DoOpenProperties(ItemMenu *This, HWND hwnd)
318 {
319         static const UINT MAX_PROP_PAGES = 99;
320         static const WCHAR wszFolder[] = {'F','o','l','d','e','r', 0};
321         static const WCHAR wszFiletypeAll[] = {'*',0};
322         LPSHELLFOLDER lpDesktopSF;
323         LPSHELLFOLDER lpSF;
324         LPDATAOBJECT lpDo;
325         WCHAR wszFiletype[MAX_PATH];
326         WCHAR wszFilename[MAX_PATH];
327         PROPSHEETHEADERW psh;
328         HPROPSHEETPAGE hpages[MAX_PROP_PAGES];
329         HPSXA hpsxa;
330         UINT ret;
331
332         TRACE("(%p)->(wnd=%p)\n", This, hwnd);
333
334         ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
335         psh.dwSize = sizeof (PROPSHEETHEADERW);
336         psh.hwndParent = hwnd;
337         psh.dwFlags = PSH_PROPTITLE;
338         psh.nPages = 0;
339         psh.u3.phpage = hpages;
340         psh.u2.nStartPage = 0;
341
342         _ILSimpleGetTextW(This->apidl[0], (LPVOID)wszFilename, MAX_PATH);
343         psh.pszCaption = (LPCWSTR)wszFilename;
344
345         /* Find out where to look for the shell extensions */
346         if (_ILIsValue(This->apidl[0]))
347         {
348             char sTemp[64];
349             sTemp[0] = 0;
350             if (_ILGetExtension(This->apidl[0], sTemp, 64))
351             {
352                 HCR_MapTypeToValueA(sTemp, sTemp, 64, TRUE);
353                 MultiByteToWideChar(CP_ACP, 0, sTemp, -1, wszFiletype, MAX_PATH);
354             }
355             else
356             {
357                 wszFiletype[0] = 0;
358             }
359         }
360         else if (_ILIsFolder(This->apidl[0]))
361         {
362             lstrcpynW(wszFiletype, wszFolder, 64);
363         }
364         else if (_ILIsSpecialFolder(This->apidl[0]))
365         {
366             LPGUID folderGUID;
367             static const WCHAR wszclsid[] = {'C','L','S','I','D','\\', 0};
368             folderGUID = _ILGetGUIDPointer(This->apidl[0]);
369             lstrcpyW(wszFiletype, wszclsid);
370             StringFromGUID2(folderGUID, &wszFiletype[6], MAX_PATH - 6);
371         }
372         else
373         {
374             FIXME("Requested properties for unknown type.\n");
375             return;
376         }
377
378         /* Get a suitable DataObject for accessing the files */
379         SHGetDesktopFolder(&lpDesktopSF);
380         if (_ILIsPidlSimple(This->pidl))
381         {
382             ret = IShellFolder_GetUIObjectOf(lpDesktopSF, hwnd, This->cidl, (LPCITEMIDLIST*)This->apidl,
383                                              &IID_IDataObject, NULL, (LPVOID *)&lpDo);
384             IShellFolder_Release(lpDesktopSF);
385         }
386         else
387         {
388             IShellFolder_BindToObject(lpDesktopSF, This->pidl, NULL, &IID_IShellFolder, (LPVOID*) &lpSF);
389             ret = IShellFolder_GetUIObjectOf(lpSF, hwnd, This->cidl, (LPCITEMIDLIST*)This->apidl,
390                                              &IID_IDataObject, NULL, (LPVOID *)&lpDo);
391             IShellFolder_Release(lpSF);
392             IShellFolder_Release(lpDesktopSF);
393         }
394
395         if (SUCCEEDED(ret))
396         {
397             hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletype, MAX_PROP_PAGES - psh.nPages, lpDo);
398             if (hpsxa != NULL)
399             {
400                 SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh);
401                 SHDestroyPropSheetExtArray(hpsxa);
402             }
403             hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletypeAll, MAX_PROP_PAGES - psh.nPages, lpDo);
404             if (hpsxa != NULL)
405             {
406                 SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh);
407                 SHDestroyPropSheetExtArray(hpsxa);
408             }
409             IDataObject_Release(lpDo);
410         }
411
412         if (psh.nPages)
413             PropertySheetW(&psh);
414         else
415             FIXME("No property pages found.\n");
416 }
417
418 static HRESULT WINAPI ItemMenu_InvokeCommand(
419         IContextMenu2 *iface,
420         LPCMINVOKECOMMANDINFO lpcmi)
421 {
422     ItemMenu *This = impl_from_IContextMenu2_Item(iface);
423
424     if (lpcmi->cbSize != sizeof(CMINVOKECOMMANDINFO))
425         FIXME("Is an EX structure\n");
426
427     TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);
428
429     if( HIWORD(lpcmi->lpVerb)==0 && LOWORD(lpcmi->lpVerb) > FCIDM_SHVIEWLAST)
430     {
431         TRACE("Invalid Verb %x\n",LOWORD(lpcmi->lpVerb));
432         return E_INVALIDARG;
433     }
434
435     if (HIWORD(lpcmi->lpVerb) == 0)
436     {
437         switch(LOWORD(lpcmi->lpVerb-This->menu.verb_offset))
438         {
439         case FCIDM_SHVIEW_EXPLORE:
440             TRACE("Verb FCIDM_SHVIEW_EXPLORE\n");
441             DoOpenExplore(This, lpcmi->hwnd, "explore");
442             break;
443         case FCIDM_SHVIEW_OPEN:
444             TRACE("Verb FCIDM_SHVIEW_OPEN\n");
445             DoOpenExplore(This, lpcmi->hwnd, "open");
446             break;
447         case FCIDM_SHVIEW_RENAME:
448         {
449             IShellBrowser *browser;
450
451             /* get the active IShellView */
452             browser = (IShellBrowser*)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
453             if (browser)
454             {
455                 IShellView *view;
456
457                 if(SUCCEEDED(IShellBrowser_QueryActiveShellView(browser, &view)))
458                 {
459                     TRACE("(shellview=%p)\n", view);
460                     IShellView_SelectItem(view, This->apidl[0],
461                          SVSI_DESELECTOTHERS|SVSI_EDIT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_SELECT);
462                     IShellView_Release(view);
463                 }
464             }
465             break;
466         }
467         case FCIDM_SHVIEW_DELETE:
468             TRACE("Verb FCIDM_SHVIEW_DELETE\n");
469             DoDelete(This);
470             break;
471         case FCIDM_SHVIEW_COPY:
472             TRACE("Verb FCIDM_SHVIEW_COPY\n");
473             DoCopyOrCut(This, lpcmi->hwnd, FALSE);
474             break;
475         case FCIDM_SHVIEW_CUT:
476             TRACE("Verb FCIDM_SHVIEW_CUT\n");
477             DoCopyOrCut(This, lpcmi->hwnd, TRUE);
478             break;
479         case FCIDM_SHVIEW_PROPERTIES:
480             TRACE("Verb FCIDM_SHVIEW_PROPERTIES\n");
481             DoOpenProperties(This, lpcmi->hwnd);
482             break;
483         default:
484             FIXME("Unhandled Verb %xl\n",LOWORD(lpcmi->lpVerb)-This->menu.verb_offset);
485             return E_INVALIDARG;
486         }
487     }
488     else
489     {
490         TRACE("Verb is %s\n",debugstr_a(lpcmi->lpVerb));
491         if (strcmp(lpcmi->lpVerb,"delete")==0)
492             DoDelete(This);
493         else if (strcmp(lpcmi->lpVerb,"properties")==0)
494             DoOpenProperties(This, lpcmi->hwnd);
495         else {
496             FIXME("Unhandled string verb %s\n",debugstr_a(lpcmi->lpVerb));
497             return E_FAIL;
498         }
499     }
500     return S_OK;
501 }
502
503 static HRESULT WINAPI ItemMenu_GetCommandString(
504         IContextMenu2 *iface,
505         UINT_PTR idCommand,
506         UINT uFlags,
507         UINT* lpReserved,
508         LPSTR lpszName,
509         UINT uMaxNameLen)
510 {
511         ItemMenu *This = impl_from_IContextMenu2_Item(iface);
512
513         HRESULT  hr = E_INVALIDARG;
514
515         TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
516
517         switch(uFlags)
518         {
519           case GCS_HELPTEXTA:
520           case GCS_HELPTEXTW:
521             hr = E_NOTIMPL;
522             break;
523
524           case GCS_VERBA:
525             switch(idCommand-This->menu.verb_offset)
526             {
527             case FCIDM_SHVIEW_RENAME:
528                 strcpy(lpszName, "rename");
529                 hr = S_OK;
530                 break;
531             }
532             break;
533
534              /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This
535              case, you need to do the lstrcpyW to the pointer passed.*/
536           case GCS_VERBW:
537             switch(idCommand-This->menu.verb_offset)
538             {
539             case FCIDM_SHVIEW_RENAME:
540                 MultiByteToWideChar( CP_ACP, 0, "rename", -1, (LPWSTR)lpszName, uMaxNameLen );
541                 hr = S_OK;
542                 break;
543             }
544             break;
545
546           case GCS_VALIDATEA:
547           case GCS_VALIDATEW:
548             hr = S_OK;
549             break;
550         }
551         TRACE("-- (%p)->(name=%s)\n",This, lpszName);
552         return hr;
553 }
554
555 /**************************************************************************
556 * NOTES
557 *  should be only in IContextMenu2 and IContextMenu3
558 *  is nevertheless called from word95
559 */
560 static HRESULT WINAPI ItemMenu_HandleMenuMsg(
561         IContextMenu2 *iface,
562         UINT uMsg,
563         WPARAM wParam,
564         LPARAM lParam)
565 {
566         ItemMenu *This = impl_from_IContextMenu2_Item(iface);
567
568         TRACE("(%p)->(msg=%x wp=%lx lp=%lx)\n",This, uMsg, wParam, lParam);
569
570         return E_NOTIMPL;
571 }
572
573 static const IContextMenu2Vtbl ItemContextMenuVtbl =
574 {
575     ItemMenu_QueryInterface,
576     ItemMenu_AddRef,
577     ItemMenu_Release,
578     ItemMenu_QueryContextMenu,
579     ItemMenu_InvokeCommand,
580     ItemMenu_GetCommandString,
581     ItemMenu_HandleMenuMsg
582 };
583
584 IContextMenu2 *ItemMenu_Constructor(IShellFolder *parent, LPCITEMIDLIST pidl, const LPCITEMIDLIST *apidl, UINT cidl)
585 {       ItemMenu* cm;
586         UINT  u;
587
588         cm = HeapAlloc(GetProcessHeap(), 0, sizeof(ItemMenu));
589         cm->menu.IContextMenu2_iface.lpVtbl = &ItemContextMenuVtbl;
590         cm->menu.ref = 1;
591         cm->menu.verb_offset = 0;
592         cm->menu.parent = parent;
593
594         cm->pidl = ILClone(pidl);
595
596         if (parent) IShellFolder_AddRef(parent);
597
598         cm->apidl = _ILCopyaPidl(apidl, cidl);
599         cm->cidl = cidl;
600
601         cm->bAllValues = 1;
602         for(u = 0; u < cidl; u++)
603           cm->bAllValues &= (_ILIsValue(apidl[u]) ? 1 : 0);
604
605         TRACE("(%p)\n",cm);
606
607         return &cm->menu.IContextMenu2_iface;
608 }
609
610 /* Background menu implementation */
611 static HRESULT WINAPI BackgroundMenu_QueryInterface(IContextMenu2 *iface, REFIID riid, void **ppvObj)
612 {
613     BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
614
615     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObj);
616
617     *ppvObj = NULL;
618
619     if(IsEqualIID(riid, &IID_IUnknown) ||
620        IsEqualIID(riid, &IID_IContextMenu) ||
621        IsEqualIID(riid, &IID_IContextMenu2))
622     {
623         *ppvObj = This;
624     }
625     else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
626     {
627         FIXME("-- LPSHELLEXTINIT pointer requested\n");
628     }
629
630     if(*ppvObj)
631     {
632         IUnknown_AddRef((IUnknown*)*ppvObj);
633         TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
634         return S_OK;
635     }
636
637     TRACE("-- Interface: E_NOINTERFACE\n");
638     return E_NOINTERFACE;
639 }
640
641 static ULONG WINAPI BackgroundMenu_AddRef(IContextMenu2 *iface)
642 {
643     BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
644     ULONG ref = InterlockedIncrement(&This->menu.ref);
645     TRACE("(%p)->(%u)\n", This, ref);
646     return ref;
647 }
648
649 static ULONG WINAPI BackgroundMenu_Release(IContextMenu2 *iface)
650 {
651     BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
652     ULONG ref = InterlockedDecrement(&This->menu.ref);
653
654     TRACE("(%p)->(%u)\n", This, ref);
655
656     if (!ref)
657     {
658         if (This->menu.parent)
659             IShellFolder_Release(This->menu.parent);
660
661         HeapFree(GetProcessHeap(), 0, This);
662     }
663
664     return ref;
665 }
666
667 static HRESULT WINAPI BackgroundMenu_QueryContextMenu(
668         IContextMenu2 *iface,
669         HMENU hMenu,
670         UINT indexMenu,
671         UINT idCmdFirst,
672         UINT idCmdLast,
673         UINT uFlags)
674 {
675     BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
676     HMENU hMyMenu;
677     UINT idMax;
678     HRESULT hr;
679
680     TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",
681           This, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
682
683     This->menu.verb_offset = idCmdFirst;
684
685     hMyMenu = LoadMenuA(shell32_hInstance, "MENU_002");
686     if (uFlags & CMF_DEFAULTONLY)
687     {
688         HMENU ourMenu = GetSubMenu(hMyMenu,0);
689         UINT oldDef = GetMenuDefaultItem(hMenu,TRUE,GMDI_USEDISABLED);
690         UINT newDef = GetMenuDefaultItem(ourMenu,TRUE,GMDI_USEDISABLED);
691         if (newDef != oldDef)
692             SetMenuDefaultItem(hMenu,newDef,TRUE);
693         if (newDef!=0xFFFFFFFF)
694             hr =  MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, newDef+1);
695         else
696             hr =  MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
697     }
698     else
699     {
700         idMax = Shell_MergeMenus (hMenu, GetSubMenu(hMyMenu,0), indexMenu,
701                                   idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS);
702         hr =  MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, idMax-idCmdFirst);
703     }
704     DestroyMenu(hMyMenu);
705
706     TRACE("(%p)->returning 0x%x\n",This,hr);
707     return hr;
708 }
709
710 static void DoNewFolder(BackgroundMenu *This, IShellView *psv)
711 {
712         ISFHelper * psfhlp;
713         WCHAR wszName[MAX_PATH];
714
715         IShellFolder_QueryInterface(This->menu.parent, &IID_ISFHelper, (LPVOID*)&psfhlp);
716         if (psfhlp)
717         {
718           LPITEMIDLIST pidl;
719           ISFHelper_GetUniqueName(psfhlp, wszName, MAX_PATH);
720           ISFHelper_AddFolder(psfhlp, 0, wszName, &pidl);
721
722           if(psv)
723           {
724             /* if we are in a shellview do labeledit */
725             IShellView_SelectItem(psv,
726                     pidl,(SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE
727                     |SVSI_FOCUSED|SVSI_SELECT));
728           }
729           SHFree(pidl);
730
731           ISFHelper_Release(psfhlp);
732         }
733 }
734
735 static BOOL DoPaste(BackgroundMenu *This)
736 {
737         BOOL bSuccess = FALSE;
738         IDataObject * pda;
739
740         TRACE("\n");
741
742         if(SUCCEEDED(OleGetClipboard(&pda)))
743         {
744           STGMEDIUM medium;
745           FORMATETC formatetc;
746
747           TRACE("pda=%p\n", pda);
748
749           /* Set the FORMATETC structure*/
750           InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLISTW), TYMED_HGLOBAL);
751
752           /* Get the pidls from IDataObject */
753           if(SUCCEEDED(IDataObject_GetData(pda,&formatetc,&medium)))
754           {
755             LPITEMIDLIST * apidl;
756             LPITEMIDLIST pidl;
757             IShellFolder *psfFrom = NULL, *psfDesktop;
758
759             LPIDA lpcida = GlobalLock(medium.u.hGlobal);
760             TRACE("cida=%p\n", lpcida);
761
762             apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
763
764             /* bind to the source shellfolder */
765             SHGetDesktopFolder(&psfDesktop);
766             if(psfDesktop)
767             {
768               IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (LPVOID*)&psfFrom);
769               IShellFolder_Release(psfDesktop);
770             }
771
772             if (psfFrom)
773             {
774               /* get source and destination shellfolder */
775               ISFHelper *psfhlpdst, *psfhlpsrc;
776               IShellFolder_QueryInterface(This->menu.parent, &IID_ISFHelper, (LPVOID*)&psfhlpdst);
777               IShellFolder_QueryInterface(psfFrom, &IID_ISFHelper, (LPVOID*)&psfhlpsrc);
778
779               /* do the copy/move */
780               if (psfhlpdst && psfhlpsrc)
781               {
782                 ISFHelper_CopyItems(psfhlpdst, psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl);
783                 /* FIXME handle move
784                 ISFHelper_DeleteItems(psfhlpsrc, lpcida->cidl, apidl);
785                 */
786               }
787               if(psfhlpdst) ISFHelper_Release(psfhlpdst);
788               if(psfhlpsrc) ISFHelper_Release(psfhlpsrc);
789               IShellFolder_Release(psfFrom);
790             }
791
792             _ILFreeaPidl(apidl, lpcida->cidl);
793             SHFree(pidl);
794
795             /* release the medium*/
796             ReleaseStgMedium(&medium);
797           }
798           IDataObject_Release(pda);
799         }
800 #if 0
801         HGLOBAL  hMem;
802
803         OpenClipboard(NULL);
804         hMem = GetClipboardData(CF_HDROP);
805
806         if(hMem)
807         {
808           char * pDropFiles = GlobalLock(hMem);
809           if(pDropFiles)
810           {
811             int len, offset = sizeof(DROPFILESTRUCT);
812
813             while( pDropFiles[offset] != 0)
814             {
815               len = strlen(pDropFiles + offset);
816               TRACE("%s\n", pDropFiles + offset);
817               offset += len+1;
818             }
819           }
820           GlobalUnlock(hMem);
821         }
822         CloseClipboard();
823 #endif
824         return bSuccess;
825 }
826
827 static HRESULT WINAPI BackgroundMenu_InvokeCommand(
828         IContextMenu2 *iface,
829         LPCMINVOKECOMMANDINFO lpcmi)
830 {
831         BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
832         LPSHELLBROWSER lpSB;
833         LPSHELLVIEW lpSV = NULL;
834         HWND hWndSV = 0;
835
836         TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);
837
838         /* get the active IShellView */
839         if((lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0)))
840         {
841           if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
842           {
843             IShellView_GetWindow(lpSV, &hWndSV);
844           }
845         }
846
847           if(HIWORD(lpcmi->lpVerb))
848           {
849             TRACE("%s\n",lpcmi->lpVerb);
850
851             if (! strcmp(lpcmi->lpVerb,CMDSTR_NEWFOLDERA))
852             {
853                 DoNewFolder(This, lpSV);
854             }
855             else if (! strcmp(lpcmi->lpVerb,CMDSTR_VIEWLISTA))
856             {
857               if(hWndSV) SendMessageA(hWndSV, WM_COMMAND, MAKEWPARAM(FCIDM_SHVIEW_LISTVIEW,0),0 );
858             }
859             else if (! strcmp(lpcmi->lpVerb,CMDSTR_VIEWDETAILSA))
860             {
861               if(hWndSV) SendMessageA(hWndSV, WM_COMMAND, MAKEWPARAM(FCIDM_SHVIEW_REPORTVIEW,0),0 );
862             }
863             else
864             {
865               FIXME("please report: unknown verb %s\n",lpcmi->lpVerb);
866             }
867           }
868           else
869           {
870             switch(LOWORD(lpcmi->lpVerb)-This->menu.verb_offset)
871             {
872               case FCIDM_SHVIEW_REFRESH:
873                 if (lpSV) IShellView_Refresh(lpSV);
874                 break;
875
876               case FCIDM_SHVIEW_NEWFOLDER:
877                 DoNewFolder(This, lpSV);
878                 break;
879
880               case FCIDM_SHVIEW_INSERT:
881                 DoPaste(This);
882                 break;
883
884               case FCIDM_SHVIEW_PROPERTIES:
885                 if (This->desktop) {
886                     ShellExecuteA(lpcmi->hwnd, "open", "rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL);
887                 } else {
888                     FIXME("launch item properties dialog\n");
889                 }
890                 break;
891
892               default:
893                 /* if it's an id just pass it to the parent shv */
894                 if (hWndSV) SendMessageA(hWndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0),0 );
895                 break;
896             }
897           }
898
899         if (lpSV)
900           IShellView_Release(lpSV);     /* QueryActiveShellView does AddRef */
901
902         return S_OK;
903 }
904
905 static HRESULT WINAPI BackgroundMenu_GetCommandString(
906         IContextMenu2 *iface,
907         UINT_PTR idCommand,
908         UINT uFlags,
909         UINT* lpReserved,
910         LPSTR lpszName,
911         UINT uMaxNameLen)
912 {
913         BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
914
915         TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
916
917         /* test the existence of the menu items, the file dialog enables
918            the buttons according to this */
919         if (uFlags == GCS_VALIDATEA)
920         {
921           if(HIWORD(idCommand))
922           {
923             if (!strcmp((LPSTR)idCommand, CMDSTR_VIEWLISTA) ||
924                 !strcmp((LPSTR)idCommand, CMDSTR_VIEWDETAILSA) ||
925                 !strcmp((LPSTR)idCommand, CMDSTR_NEWFOLDERA))
926             {
927               return S_OK;
928             }
929           }
930         }
931
932         FIXME("unknown command string\n");
933         return E_FAIL;
934 }
935
936 static HRESULT WINAPI BackgroundMenu_HandleMenuMsg(
937         IContextMenu2 *iface,
938         UINT uMsg,
939         WPARAM wParam,
940         LPARAM lParam)
941 {
942     BackgroundMenu *This = impl_from_IContextMenu2_Back(iface);
943     FIXME("(%p)->(msg=%x wp=%lx lp=%lx)\n",This, uMsg, wParam, lParam);
944     return E_NOTIMPL;
945 }
946
947 static const IContextMenu2Vtbl BackgroundContextMenuVtbl =
948 {
949     BackgroundMenu_QueryInterface,
950     BackgroundMenu_AddRef,
951     BackgroundMenu_Release,
952     BackgroundMenu_QueryContextMenu,
953     BackgroundMenu_InvokeCommand,
954     BackgroundMenu_GetCommandString,
955     BackgroundMenu_HandleMenuMsg
956 };
957
958 IContextMenu2 *BackgroundMenu_Constructor(IShellFolder *parent, BOOL desktop)
959 {
960     BackgroundMenu *cm;
961
962     cm = HeapAlloc(GetProcessHeap(), 0, sizeof(*cm));
963     cm->menu.IContextMenu2_iface.lpVtbl = &BackgroundContextMenuVtbl;
964     cm->menu.ref = 1;
965     cm->menu.parent = parent;
966     cm->menu.verb_offset = 0;
967
968     cm->desktop = desktop;
969     if (parent) IShellFolder_AddRef(parent);
970
971     TRACE("(%p)->()\n", cm);
972     return &cm->menu.IContextMenu2_iface;
973 }