Added a framework for testing CreateProcess and a few tests.
[wine] / dlls / shell32 / shv_item_cmenu.c
1 /*
2  *      IContextMenu for items in the shellview
3  *
4  * Copyright 1998, 2000 Juergen Schmied <juergen.schmied@debitel.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <string.h>
22
23 #include "winerror.h"
24 #include "wine/debug.h"
25
26 #include "pidl.h"
27 #include "shlguid.h"
28 #include "wine/obj_base.h"
29 #include "wine/obj_contextmenu.h"
30 #include "wine/obj_shellbrowser.h"
31 #include "wine/obj_shellextinit.h"
32 #include "undocshell.h"
33
34 #include "shell32_main.h"
35 #include "shellfolder.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 /**************************************************************************
40 *  IContextMenu Implementation
41 */
42 typedef struct 
43 {       ICOM_VFIELD(IContextMenu);
44         DWORD           ref;
45         IShellFolder*   pSFParent;
46         LPITEMIDLIST    pidl;           /* root pidl */
47         LPITEMIDLIST    *apidl;         /* array of child pidls */
48         UINT            cidl;
49         BOOL            bAllValues;
50 } ItemCmImpl;
51
52
53 static struct ICOM_VTABLE(IContextMenu) cmvt;
54
55 /**************************************************************************
56 * ISvItemCm_CanRenameItems()
57 */
58 static BOOL ISvItemCm_CanRenameItems(ItemCmImpl *This)
59 {       UINT  i;
60         DWORD dwAttributes;
61
62         TRACE("(%p)->()\n",This);
63
64         if(This->apidl)
65         {
66           for(i = 0; i < This->cidl; i++){}
67           if(i > 1) return FALSE;               /* can't rename more than one item at a time*/
68           dwAttributes = SFGAO_CANRENAME;
69           IShellFolder_GetAttributesOf(This->pSFParent, 1, This->apidl, &dwAttributes);
70           return dwAttributes & SFGAO_CANRENAME;
71         }
72         return FALSE;
73 }
74
75 /**************************************************************************
76 *   ISvItemCm_Constructor()
77 */
78 IContextMenu *ISvItemCm_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST pidl, LPCITEMIDLIST *apidl, UINT cidl)
79 {       ItemCmImpl* cm;
80         UINT  u;
81
82         cm = (ItemCmImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ItemCmImpl));
83         ICOM_VTBL(cm)=&cmvt;
84         cm->ref = 1;
85         cm->pidl = ILClone(pidl);
86         cm->pSFParent = pSFParent;
87
88         if(pSFParent) IShellFolder_AddRef(pSFParent);
89
90         cm->apidl = _ILCopyaPidl(apidl, cidl);
91         cm->cidl = cidl;
92
93         cm->bAllValues = 1;
94         for(u = 0; u < cidl; u++)
95         {
96           cm->bAllValues &= (_ILIsValue(apidl[u]) ? 1 : 0);
97         }
98
99         TRACE("(%p)->()\n",cm);
100
101         shell32_ObjCount++;
102         return (IContextMenu*)cm;
103 }
104
105 /**************************************************************************
106 *  ISvItemCm_fnQueryInterface
107 */
108 static HRESULT WINAPI ISvItemCm_fnQueryInterface(IContextMenu *iface, REFIID riid, LPVOID *ppvObj)
109 {
110         ICOM_THIS(ItemCmImpl, iface);
111
112         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
113
114         *ppvObj = NULL;
115
116         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
117         {
118           *ppvObj = This; 
119         }
120         else if(IsEqualIID(riid, &IID_IContextMenu))  /*IContextMenu*/
121         {
122           *ppvObj = This;
123         }   
124         else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
125         {
126           FIXME("-- LPSHELLEXTINIT pointer requested\n");
127         }
128
129         if(*ppvObj)
130         { 
131           IUnknown_AddRef((IUnknown*)*ppvObj); 
132           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
133           return S_OK;
134         }
135         TRACE("-- Interface: E_NOINTERFACE\n");
136         return E_NOINTERFACE;
137 }
138
139 /**************************************************************************
140 *  ISvItemCm_fnAddRef
141 */
142 static ULONG WINAPI ISvItemCm_fnAddRef(IContextMenu *iface)
143 {
144         ICOM_THIS(ItemCmImpl, iface);
145
146         TRACE("(%p)->(count=%lu)\n",This, This->ref);
147
148         shell32_ObjCount++;
149         return ++(This->ref);
150 }
151
152 /**************************************************************************
153 *  ISvItemCm_fnRelease
154 */
155 static ULONG WINAPI ISvItemCm_fnRelease(IContextMenu *iface)
156 {
157         ICOM_THIS(ItemCmImpl, iface);
158
159         TRACE("(%p)->()\n",This);
160
161         shell32_ObjCount--;
162
163         if (!--(This->ref)) 
164         {
165           TRACE(" destroying IContextMenu(%p)\n",This);
166
167           if(This->pSFParent)
168             IShellFolder_Release(This->pSFParent);
169
170           if(This->pidl)
171             SHFree(This->pidl);
172           
173           /*make sure the pidl is freed*/
174           _ILFreeaPidl(This->apidl, This->cidl);
175
176           HeapFree(GetProcessHeap(),0,This);
177           return 0;
178         }
179         return This->ref;
180 }
181
182 /**************************************************************************
183 *  ICM_InsertItem()
184 */ 
185 void WINAPI _InsertMenuItem (
186         HMENU hmenu,
187         UINT indexMenu,
188         BOOL fByPosition,
189         UINT wID,
190         UINT fType,
191         LPSTR dwTypeData,
192         UINT fState)
193 {
194         MENUITEMINFOA   mii;
195
196         ZeroMemory(&mii, sizeof(mii));
197         mii.cbSize = sizeof(mii);
198         if (fType == MFT_SEPARATOR)
199         {
200           mii.fMask = MIIM_ID | MIIM_TYPE;
201         }
202         else
203         {
204           mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
205           mii.dwTypeData = dwTypeData;
206           mii.fState = fState;
207         }
208         mii.wID = wID;
209         mii.fType = fType;
210         InsertMenuItemA( hmenu, indexMenu, fByPosition, &mii);
211 }
212 /**************************************************************************
213 * ISvItemCm_fnQueryContextMenu()
214 */
215
216 static HRESULT WINAPI ISvItemCm_fnQueryContextMenu(
217         IContextMenu *iface,
218         HMENU hmenu,
219         UINT indexMenu,
220         UINT idCmdFirst,
221         UINT idCmdLast,
222         UINT uFlags)
223 {
224         ICOM_THIS(ItemCmImpl, iface);
225
226         TRACE("(%p)->(hmenu=%x indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
227
228         if(!(CMF_DEFAULTONLY & uFlags))
229         {
230           if(uFlags & CMF_EXPLORE)
231           {
232             if(This->bAllValues)
233             {
234               _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
235               _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED|MFS_DEFAULT);
236             }
237             else
238             {
239               _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED|MFS_DEFAULT);
240               _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
241             }
242           }
243           else
244           {
245             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Select", MFS_ENABLED|MFS_DEFAULT);
246           }
247           _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
248           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, "&Copy", MFS_ENABLED);
249           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, "&Cut", MFS_ENABLED);
250
251           _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
252           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, "&Delete", MFS_ENABLED);
253
254           if(uFlags & CMF_CANRENAME)
255             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, "&Rename", ISvItemCm_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED);
256
257           return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (FCIDM_SHVIEWLAST));
258         }
259         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
260 }
261
262 /**************************************************************************
263 * DoOpenExplore
264 *
265 *  for folders only
266 */
267
268 static void DoOpenExplore(
269         IContextMenu *iface,
270         HWND hwnd,
271         LPCSTR verb)
272 {
273         ICOM_THIS(ItemCmImpl, iface);
274
275         int i, bFolderFound = FALSE;
276         LPITEMIDLIST    pidlFQ;
277         SHELLEXECUTEINFOA       sei;
278
279         /* Find the first item in the list that is not a value. These commands 
280             should never be invoked if there isn't at least one folder item in the list.*/
281
282         for(i = 0; i<This->cidl; i++)
283         {
284           if(!_ILIsValue(This->apidl[i]))
285           {
286             bFolderFound = TRUE;
287             break;
288           }
289         }
290
291         if (!bFolderFound) return;
292
293         pidlFQ = ILCombine(This->pidl, This->apidl[i]);
294
295         ZeroMemory(&sei, sizeof(sei));
296         sei.cbSize = sizeof(sei);
297         sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
298         sei.lpIDList = pidlFQ;
299         sei.lpClass = "folder";
300         sei.hwnd = hwnd;
301         sei.nShow = SW_SHOWNORMAL;
302         sei.lpVerb = verb;
303         ShellExecuteExA(&sei);
304         SHFree(pidlFQ);
305 }
306
307 /**************************************************************************
308 * DoRename
309 */
310 static void DoRename(
311         IContextMenu *iface,
312         HWND hwnd)
313 {
314         ICOM_THIS(ItemCmImpl, iface);
315
316         LPSHELLBROWSER  lpSB;
317         LPSHELLVIEW     lpSV;
318
319         TRACE("(%p)->(wnd=%x)\n",This, hwnd);    
320
321         /* get the active IShellView */
322         if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
323         {
324           if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
325           {
326             TRACE("(sv=%p)\n",lpSV);    
327             IShellView_SelectItem(lpSV, This->apidl[0],
328               SVSI_DESELECTOTHERS|SVSI_EDIT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_SELECT);
329             IShellView_Release(lpSV);
330           }
331         }
332 }
333
334 /**************************************************************************
335  * DoDelete
336  *
337  * deletes the currently selected items
338  */
339 static void DoDelete(IContextMenu *iface)
340 {
341         ICOM_THIS(ItemCmImpl, iface);
342         ISFHelper * psfhlp;
343         
344         IShellFolder_QueryInterface(This->pSFParent, &IID_ISFHelper, (LPVOID*)&psfhlp);
345         if (psfhlp)
346         {
347           ISFHelper_DeleteItems(psfhlp, This->cidl, This->apidl);
348           ISFHelper_Release(psfhlp);
349         }
350 }
351
352 /**************************************************************************
353  * DoCopyOrCut
354  *
355  * copies the currently selected items into the clipboard
356  */
357 static BOOL DoCopyOrCut(
358         IContextMenu *iface,
359         HWND hwnd,
360         BOOL bCut)
361 {
362         ICOM_THIS(ItemCmImpl, iface);
363
364         LPSHELLBROWSER  lpSB;
365         LPSHELLVIEW     lpSV;
366         LPDATAOBJECT    lpDo;
367         
368         TRACE("(%p)->(wnd=0x%04x,bCut=0x%08x)\n",This, hwnd, bCut);
369
370         if(GetShellOle())
371         {
372           /* get the active IShellView */
373           if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
374           {
375             if (SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
376             {
377               if (SUCCEEDED(IShellView_GetItemObject(lpSV, SVGIO_SELECTION, &IID_IDataObject, (LPVOID*)&lpDo)))
378               {
379                 pOleSetClipboard(lpDo);
380                 IDataObject_Release(lpDo);
381               }
382               IShellView_Release(lpSV);
383             }
384           }
385         }
386         return TRUE;
387 #if 0
388 /*
389   the following code does the copy operation witout ole32.dll
390   we might need this possibility too (js)
391 */
392         BOOL bSuccess = FALSE;
393
394         TRACE("(%p)\n", iface);
395         
396         if(OpenClipboard(NULL))
397         {
398           if(EmptyClipboard())
399           {
400             IPersistFolder2 * ppf2;
401             IShellFolder_QueryInterface(This->pSFParent, &IID_IPersistFolder2, (LPVOID*)&ppf2);
402             if (ppf2)
403             {
404               LPITEMIDLIST pidl;
405               IPersistFolder2_GetCurFolder(ppf2, &pidl);
406               if(pidl)
407               {
408                 HGLOBAL hMem;
409
410                 hMem = RenderHDROP(pidl, This->apidl, This->cidl);
411                 
412                 if(SetClipboardData(CF_HDROP, hMem))
413                 {
414                   bSuccess = TRUE;
415                 }
416                 SHFree(pidl);
417               }
418               IPersistFolder2_Release(ppf2);
419             }
420             
421           }
422           CloseClipboard();
423         }
424         return bSuccess;
425 #endif
426 }
427 /**************************************************************************
428 * ISvItemCm_fnInvokeCommand()
429 */
430 static HRESULT WINAPI ISvItemCm_fnInvokeCommand(
431         IContextMenu *iface,
432         LPCMINVOKECOMMANDINFO lpcmi)
433 {
434         ICOM_THIS(ItemCmImpl, iface);
435
436         TRACE("(%p)->(invcom=%p verb=%p wnd=%x)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);    
437
438         if(LOWORD(lpcmi->lpVerb) > FCIDM_SHVIEWLAST)  return E_INVALIDARG;
439
440         switch(LOWORD(lpcmi->lpVerb))
441         {
442           case FCIDM_SHVIEW_EXPLORE:
443             DoOpenExplore(iface, lpcmi->hwnd, "explore");
444             break;
445           case FCIDM_SHVIEW_OPEN:
446             DoOpenExplore(iface, lpcmi->hwnd, "open");
447             break;
448           case FCIDM_SHVIEW_RENAME:
449             DoRename(iface, lpcmi->hwnd);
450             break;          
451           case FCIDM_SHVIEW_DELETE:
452             DoDelete(iface);
453             break;          
454           case FCIDM_SHVIEW_COPY:
455             DoCopyOrCut(iface, lpcmi->hwnd, FALSE);
456             break;
457           case FCIDM_SHVIEW_CUT:
458             DoCopyOrCut(iface, lpcmi->hwnd, TRUE);
459             break;
460         }
461         return NOERROR;
462 }
463
464 /**************************************************************************
465 *  ISvItemCm_fnGetCommandString()
466 */
467 static HRESULT WINAPI ISvItemCm_fnGetCommandString(
468         IContextMenu *iface,
469         UINT idCommand,
470         UINT uFlags,
471         LPUINT lpReserved,
472         LPSTR lpszName,
473         UINT uMaxNameLen)
474 {       
475         ICOM_THIS(ItemCmImpl, iface);
476
477         HRESULT  hr = E_INVALIDARG;
478
479         TRACE("(%p)->(idcom=%x flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
480
481         switch(uFlags)
482         {
483           case GCS_HELPTEXT:
484             hr = E_NOTIMPL;
485             break;
486
487           case GCS_VERBA:
488             switch(idCommand)
489             {
490               case FCIDM_SHVIEW_RENAME:
491                 strcpy((LPSTR)lpszName, "rename");
492                 hr = NOERROR;
493                 break;
494             }
495             break;
496
497              /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This 
498              case, you need to do the lstrcpyW to the pointer passed.*/
499           case GCS_VERBW:
500             switch(idCommand)
501             { case FCIDM_SHVIEW_RENAME:
502                 MultiByteToWideChar( CP_ACP, 0, "rename", -1, (LPWSTR)lpszName, uMaxNameLen );
503                 hr = NOERROR;
504                 break;
505             }
506             break;
507
508           case GCS_VALIDATE:
509             hr = NOERROR;
510             break;
511         }
512         TRACE("-- (%p)->(name=%s)\n",This, lpszName);
513         return hr;
514 }
515
516 /**************************************************************************
517 * ISvItemCm_fnHandleMenuMsg()
518 * NOTES
519 *  should be only in IContextMenu2 and IContextMenu3
520 *  is nevertheless called from word95
521 */
522 static HRESULT WINAPI ISvItemCm_fnHandleMenuMsg(
523         IContextMenu *iface,
524         UINT uMsg,
525         WPARAM wParam,
526         LPARAM lParam)
527 {
528         ICOM_THIS(ItemCmImpl, iface);
529
530         TRACE("(%p)->(msg=%x wp=%x lp=%lx)\n",This, uMsg, wParam, lParam);
531
532         return E_NOTIMPL;
533 }
534
535 static struct ICOM_VTABLE(IContextMenu) cmvt = 
536 {       
537         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
538         ISvItemCm_fnQueryInterface,
539         ISvItemCm_fnAddRef,
540         ISvItemCm_fnRelease,
541         ISvItemCm_fnQueryContextMenu,
542         ISvItemCm_fnInvokeCommand,
543         ISvItemCm_fnGetCommandString,
544         ISvItemCm_fnHandleMenuMsg,
545         (void *) 0xdeadbabe     /* just paranoia */
546 };