shell32: Win64 printf format warning fixes.
[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 "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 {       const IContextMenu2Vtbl *lpVtbl;
47         LONG            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 const 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=%u)\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=%i)\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           SHFree(This->pidl);
170
171           /*make sure the pidl is freed*/
172           _ILFreeaPidl(This->apidl, This->cidl);
173
174           HeapFree(GetProcessHeap(),0,This);
175         }
176         return refCount;
177 }
178
179 /**************************************************************************
180 *  ICM_InsertItem()
181 */
182 void WINAPI _InsertMenuItem (
183         HMENU hmenu,
184         UINT indexMenu,
185         BOOL fByPosition,
186         UINT wID,
187         UINT fType,
188         LPCSTR dwTypeData,
189         UINT fState)
190 {
191         MENUITEMINFOA   mii;
192
193         ZeroMemory(&mii, sizeof(mii));
194         mii.cbSize = sizeof(mii);
195         if (fType == MFT_SEPARATOR)
196         {
197           mii.fMask = MIIM_ID | MIIM_TYPE;
198         }
199         else
200         {
201           mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
202           mii.dwTypeData = (LPSTR) dwTypeData;
203           mii.fState = fState;
204         }
205         mii.wID = wID;
206         mii.fType = fType;
207         InsertMenuItemA( hmenu, indexMenu, fByPosition, &mii);
208 }
209
210 /**************************************************************************
211 * ISvItemCm_fnQueryContextMenu()
212 * FIXME: load menu MENU_SHV_FILE out of resources instead if creating
213 *                each menu item by calling _InsertMenuItem()
214 */
215 static HRESULT WINAPI ISvItemCm_fnQueryContextMenu(
216         IContextMenu2 *iface,
217         HMENU hmenu,
218         UINT indexMenu,
219         UINT idCmdFirst,
220         UINT idCmdLast,
221         UINT uFlags)
222 {
223         ItemCmImpl *This = (ItemCmImpl *)iface;
224
225         TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
226
227         if (idCmdFirst != 0)
228           FIXME("We should use idCmdFirst=%d and idCmdLast=%d for command ids\n", idCmdFirst, idCmdLast);
229
230         if(!(CMF_DEFAULTONLY & uFlags) && This->cidl>0)
231         {
232           if(!(uFlags & CMF_EXPLORE))
233             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Select", MFS_ENABLED);
234
235           if(This->bAllValues)
236           {
237             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
238             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED);
239           }
240           else
241           {
242             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED);
243             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
244           }
245
246           SetMenuDefaultItem(hmenu, 0, MF_BYPOSITION);
247
248           _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
249           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, "&Copy", MFS_ENABLED);
250           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, "&Cut", MFS_ENABLED);
251
252           _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
253           _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, "&Delete", MFS_ENABLED);
254
255           if(uFlags & CMF_CANRENAME)
256             _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, "&Rename", ISvItemCm_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED);
257
258           return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (FCIDM_SHVIEWLAST));
259         }
260         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
261 }
262
263 /**************************************************************************
264 * DoOpenExplore
265 *
266 *  for folders only
267 */
268
269 static void DoOpenExplore(
270         IContextMenu2 *iface,
271         HWND hwnd,
272         LPCSTR verb)
273 {
274         ItemCmImpl *This = (ItemCmImpl *)iface;
275
276         UINT i, bFolderFound = FALSE;
277         LPITEMIDLIST    pidlFQ;
278         SHELLEXECUTEINFOA       sei;
279
280         /* Find the first item in the list that is not a value. These commands
281             should never be invoked if there isn't at least one folder item in the list.*/
282
283         for(i = 0; i<This->cidl; i++)
284         {
285           if(!_ILIsValue(This->apidl[i]))
286           {
287             bFolderFound = TRUE;
288             break;
289           }
290         }
291
292         if (!bFolderFound) return;
293
294         pidlFQ = ILCombine(This->pidl, This->apidl[i]);
295
296         ZeroMemory(&sei, sizeof(sei));
297         sei.cbSize = sizeof(sei);
298         sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
299         sei.lpIDList = pidlFQ;
300         sei.lpClass = "Folder";
301         sei.hwnd = hwnd;
302         sei.nShow = SW_SHOWNORMAL;
303         sei.lpVerb = verb;
304         ShellExecuteExA(&sei);
305         SHFree(pidlFQ);
306 }
307
308 /**************************************************************************
309 * DoRename
310 */
311 static void DoRename(
312         IContextMenu2 *iface,
313         HWND hwnd)
314 {
315         ItemCmImpl *This = (ItemCmImpl *)iface;
316
317         LPSHELLBROWSER  lpSB;
318         LPSHELLVIEW     lpSV;
319
320         TRACE("(%p)->(wnd=%p)\n",This, hwnd);
321
322         /* get the active IShellView */
323         if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
324         {
325           if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
326           {
327             TRACE("(sv=%p)\n",lpSV);
328             IShellView_SelectItem(lpSV, This->apidl[0],
329               SVSI_DESELECTOTHERS|SVSI_EDIT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_SELECT);
330             IShellView_Release(lpSV);
331           }
332         }
333 }
334
335 /**************************************************************************
336  * DoDelete
337  *
338  * deletes the currently selected items
339  */
340 static void DoDelete(IContextMenu2 *iface)
341 {
342         ItemCmImpl *This = (ItemCmImpl *)iface;
343         ISFHelper * psfhlp;
344
345         IShellFolder_QueryInterface(This->pSFParent, &IID_ISFHelper, (LPVOID*)&psfhlp);
346         if (psfhlp)
347         {
348           ISFHelper_DeleteItems(psfhlp, This->cidl, (LPCITEMIDLIST *)This->apidl);
349           ISFHelper_Release(psfhlp);
350         }
351 }
352
353 /**************************************************************************
354  * DoCopyOrCut
355  *
356  * copies the currently selected items into the clipboard
357  */
358 static BOOL DoCopyOrCut(
359         IContextMenu2 *iface,
360         HWND hwnd,
361         BOOL bCut)
362 {
363         ItemCmImpl *This = (ItemCmImpl *)iface;
364
365         LPSHELLBROWSER  lpSB;
366         LPSHELLVIEW     lpSV;
367         LPDATAOBJECT    lpDo;
368
369         TRACE("(%p)->(wnd=%p,bCut=0x%08x)\n",This, hwnd, bCut);
370
371         /* get the active IShellView */
372         if ((lpSB = (LPSHELLBROWSER)SendMessageA(hwnd, CWM_GETISHELLBROWSER,0,0)))
373         {
374           if (SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB, &lpSV)))
375           {
376             if (SUCCEEDED(IShellView_GetItemObject(lpSV, SVGIO_SELECTION, &IID_IDataObject, (LPVOID*)&lpDo)))
377             {
378               OleSetClipboard(lpDo);
379               IDataObject_Release(lpDo);
380             }
381             IShellView_Release(lpSV);
382           }
383         }
384         return TRUE;
385 }
386 /**************************************************************************
387 * ISvItemCm_fnInvokeCommand()
388 */
389 static HRESULT WINAPI ISvItemCm_fnInvokeCommand(
390         IContextMenu2 *iface,
391         LPCMINVOKECOMMANDINFO lpcmi)
392 {
393     ItemCmImpl *This = (ItemCmImpl *)iface;
394
395     if (lpcmi->cbSize != sizeof(CMINVOKECOMMANDINFO))
396         FIXME("Is an EX structure\n");
397
398     TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);
399
400     if( HIWORD(lpcmi->lpVerb)==0 && LOWORD(lpcmi->lpVerb) > FCIDM_SHVIEWLAST)
401     {
402         TRACE("Invalid Verb %x\n",LOWORD(lpcmi->lpVerb));
403         return E_INVALIDARG;
404     }
405
406     if (HIWORD(lpcmi->lpVerb) == 0)
407     {
408         switch(LOWORD(lpcmi->lpVerb))
409         {
410         case FCIDM_SHVIEW_EXPLORE:
411             TRACE("Verb FCIDM_SHVIEW_EXPLORE\n");
412             DoOpenExplore(iface, lpcmi->hwnd, "explore");
413             break;
414         case FCIDM_SHVIEW_OPEN:
415             TRACE("Verb FCIDM_SHVIEW_OPEN\n");
416             DoOpenExplore(iface, lpcmi->hwnd, "open");
417             break;
418         case FCIDM_SHVIEW_RENAME:
419             TRACE("Verb FCIDM_SHVIEW_RENAME\n");
420             DoRename(iface, lpcmi->hwnd);
421             break;
422         case FCIDM_SHVIEW_DELETE:
423             TRACE("Verb FCIDM_SHVIEW_DELETE\n");
424             DoDelete(iface);
425             break;
426         case FCIDM_SHVIEW_COPY:
427             TRACE("Verb FCIDM_SHVIEW_COPY\n");
428             DoCopyOrCut(iface, lpcmi->hwnd, FALSE);
429             break;
430         case FCIDM_SHVIEW_CUT:
431             TRACE("Verb FCIDM_SHVIEW_CUT\n");
432             DoCopyOrCut(iface, lpcmi->hwnd, TRUE);
433             break;
434         default:
435             FIXME("Unhandled Verb %xl\n",LOWORD(lpcmi->lpVerb));
436         }
437     }
438     else
439     {
440         TRACE("Verb is %s\n",debugstr_a(lpcmi->lpVerb));
441         if (strcmp(lpcmi->lpVerb,"delete")==0)
442             DoDelete(iface);
443         else
444             FIXME("Unhandled string verb %s\n",debugstr_a(lpcmi->lpVerb));
445     }
446     return NOERROR;
447 }
448
449 /**************************************************************************
450 *  ISvItemCm_fnGetCommandString()
451 */
452 static HRESULT WINAPI ISvItemCm_fnGetCommandString(
453         IContextMenu2 *iface,
454         UINT_PTR idCommand,
455         UINT uFlags,
456         UINT* lpReserved,
457         LPSTR lpszName,
458         UINT uMaxNameLen)
459 {
460         ItemCmImpl *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_HELPTEXTA:
469           case GCS_HELPTEXTW:
470             hr = E_NOTIMPL;
471             break;
472
473           case GCS_VERBA:
474             switch(idCommand)
475             {
476               case FCIDM_SHVIEW_RENAME:
477                 strcpy((LPSTR)lpszName, "rename");
478                 hr = NOERROR;
479                 break;
480             }
481             break;
482
483              /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This
484              case, you need to do the lstrcpyW to the pointer passed.*/
485           case GCS_VERBW:
486             switch(idCommand)
487             { case FCIDM_SHVIEW_RENAME:
488                 MultiByteToWideChar( CP_ACP, 0, "rename", -1, (LPWSTR)lpszName, uMaxNameLen );
489                 hr = NOERROR;
490                 break;
491             }
492             break;
493
494           case GCS_VALIDATEA:
495           case GCS_VALIDATEW:
496             hr = NOERROR;
497             break;
498         }
499         TRACE("-- (%p)->(name=%s)\n",This, lpszName);
500         return hr;
501 }
502
503 /**************************************************************************
504 * ISvItemCm_fnHandleMenuMsg()
505 * NOTES
506 *  should be only in IContextMenu2 and IContextMenu3
507 *  is nevertheless called from word95
508 */
509 static HRESULT WINAPI ISvItemCm_fnHandleMenuMsg(
510         IContextMenu2 *iface,
511         UINT uMsg,
512         WPARAM wParam,
513         LPARAM lParam)
514 {
515         ItemCmImpl *This = (ItemCmImpl *)iface;
516
517         TRACE("(%p)->(msg=%x wp=%x lp=%lx)\n",This, uMsg, wParam, lParam);
518
519         return E_NOTIMPL;
520 }
521
522 static const IContextMenu2Vtbl cmvt =
523 {
524         ISvItemCm_fnQueryInterface,
525         ISvItemCm_fnAddRef,
526         ISvItemCm_fnRelease,
527         ISvItemCm_fnQueryContextMenu,
528         ISvItemCm_fnInvokeCommand,
529         ISvItemCm_fnGetCommandString,
530         ISvItemCm_fnHandleMenuMsg
531 };