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