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