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