SHGetFileInfo should tolerate null pointers.
[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 "pidl.h"
29 #include "shlguid.h"
30 #include "undocshell.h"
31 #include "shlobj.h"
32
33 #include "shell32_main.h"
34 #include "shellfolder.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
37
38 /**************************************************************************
39 *  IContextMenu Implementation
40 */
41 typedef struct
42 {       ICOM_VFIELD(IContextMenu);
43         DWORD           ref;
44         IShellFolder*   pSFParent;
45         LPITEMIDLIST    pidl;           /* root pidl */
46         LPITEMIDLIST    *apidl;         /* array of child pidls */
47         UINT            cidl;
48         BOOL            bAllValues;
49 } ItemCmImpl;
50
51
52 static struct ICOM_VTABLE(IContextMenu) cmvt;
53
54 /**************************************************************************
55 * ISvItemCm_CanRenameItems()
56 */
57 static BOOL ISvItemCm_CanRenameItems(ItemCmImpl *This)
58 {       UINT  i;
59         DWORD dwAttributes;
60
61         TRACE("(%p)->()\n",This);
62
63         if(This->apidl)
64         {
65           for(i = 0; i < This->cidl; i++){}
66           if(i > 1) return FALSE;               /* can't rename more than one item at a time*/
67           dwAttributes = SFGAO_CANRENAME;
68           IShellFolder_GetAttributesOf(This->pSFParent, 1, This->apidl, &dwAttributes);
69           return dwAttributes & SFGAO_CANRENAME;
70         }
71         return FALSE;
72 }
73
74 /**************************************************************************
75 *   ISvItemCm_Constructor()
76 */
77 IContextMenu *ISvItemCm_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST pidl, LPCITEMIDLIST *apidl, UINT cidl)
78 {       ItemCmImpl* cm;
79         UINT  u;
80
81         cm = (ItemCmImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ItemCmImpl));
82         cm->lpVtbl = &cmvt;
83         cm->ref = 1;
84         cm->pidl = ILClone(pidl);
85         cm->pSFParent = pSFParent;
86
87         if(pSFParent) IShellFolder_AddRef(pSFParent);
88
89         cm->apidl = _ILCopyaPidl(apidl, cidl);
90         cm->cidl = cidl;
91
92         cm->bAllValues = 1;
93         for(u = 0; u < cidl; u++)
94         {
95           cm->bAllValues &= (_ILIsValue(apidl[u]) ? 1 : 0);
96         }
97
98         TRACE("(%p)->()\n",cm);
99
100         return (IContextMenu*)cm;
101 }
102
103 /**************************************************************************
104 *  ISvItemCm_fnQueryInterface
105 */
106 static HRESULT WINAPI ISvItemCm_fnQueryInterface(IContextMenu *iface, REFIID riid, LPVOID *ppvObj)
107 {
108         ICOM_THIS(ItemCmImpl, iface);
109
110         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
111
112         *ppvObj = NULL;
113
114         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
115         {
116           *ppvObj = This;
117         }
118         else if(IsEqualIID(riid, &IID_IContextMenu))  /*IContextMenu*/
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(IContextMenu *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(IContextMenu *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         IContextMenu *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         IContextMenu *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         IContextMenu *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(IContextMenu *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, 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         IContextMenu *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         IContextMenu *iface,
427         LPCMINVOKECOMMANDINFO lpcmi)
428 {
429         ICOM_THIS(ItemCmImpl, iface);
430
431         TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);
432
433         if(LOWORD(lpcmi->lpVerb) > FCIDM_SHVIEWLAST)  return E_INVALIDARG;
434
435         switch(LOWORD(lpcmi->lpVerb))
436         {
437           case FCIDM_SHVIEW_EXPLORE:
438             DoOpenExplore(iface, lpcmi->hwnd, "explore");
439             break;
440           case FCIDM_SHVIEW_OPEN:
441             DoOpenExplore(iface, lpcmi->hwnd, "open");
442             break;
443           case FCIDM_SHVIEW_RENAME:
444             DoRename(iface, lpcmi->hwnd);
445             break;
446           case FCIDM_SHVIEW_DELETE:
447             DoDelete(iface);
448             break;
449           case FCIDM_SHVIEW_COPY:
450             DoCopyOrCut(iface, lpcmi->hwnd, FALSE);
451             break;
452           case FCIDM_SHVIEW_CUT:
453             DoCopyOrCut(iface, lpcmi->hwnd, TRUE);
454             break;
455         }
456         return NOERROR;
457 }
458
459 /**************************************************************************
460 *  ISvItemCm_fnGetCommandString()
461 */
462 static HRESULT WINAPI ISvItemCm_fnGetCommandString(
463         IContextMenu *iface,
464         UINT idCommand,
465         UINT uFlags,
466         UINT* lpReserved,
467         LPSTR lpszName,
468         UINT uMaxNameLen)
469 {
470         ICOM_THIS(ItemCmImpl, iface);
471
472         HRESULT  hr = E_INVALIDARG;
473
474         TRACE("(%p)->(idcom=%x flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
475
476         switch(uFlags)
477         {
478           case GCS_HELPTEXT:
479             hr = E_NOTIMPL;
480             break;
481
482           case GCS_VERBA:
483             switch(idCommand)
484             {
485               case FCIDM_SHVIEW_RENAME:
486                 strcpy((LPSTR)lpszName, "rename");
487                 hr = NOERROR;
488                 break;
489             }
490             break;
491
492              /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This
493              case, you need to do the lstrcpyW to the pointer passed.*/
494           case GCS_VERBW:
495             switch(idCommand)
496             { case FCIDM_SHVIEW_RENAME:
497                 MultiByteToWideChar( CP_ACP, 0, "rename", -1, (LPWSTR)lpszName, uMaxNameLen );
498                 hr = NOERROR;
499                 break;
500             }
501             break;
502
503           case GCS_VALIDATE:
504             hr = NOERROR;
505             break;
506         }
507         TRACE("-- (%p)->(name=%s)\n",This, lpszName);
508         return hr;
509 }
510
511 /**************************************************************************
512 * ISvItemCm_fnHandleMenuMsg()
513 * NOTES
514 *  should be only in IContextMenu2 and IContextMenu3
515 *  is nevertheless called from word95
516 */
517 static HRESULT WINAPI ISvItemCm_fnHandleMenuMsg(
518         IContextMenu *iface,
519         UINT uMsg,
520         WPARAM wParam,
521         LPARAM lParam)
522 {
523         ICOM_THIS(ItemCmImpl, iface);
524
525         TRACE("(%p)->(msg=%x wp=%x lp=%lx)\n",This, uMsg, wParam, lParam);
526
527         return E_NOTIMPL;
528 }
529
530 static struct ICOM_VTABLE(IContextMenu) cmvt =
531 {
532         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
533         ISvItemCm_fnQueryInterface,
534         ISvItemCm_fnAddRef,
535         ISvItemCm_fnRelease,
536         ISvItemCm_fnQueryContextMenu,
537         ISvItemCm_fnInvokeCommand,
538         ISvItemCm_fnGetCommandString,
539         ISvItemCm_fnHandleMenuMsg,
540         (void *) 0xdeadbabe     /* just paranoia */
541 };