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