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