Added stubs for DAD_SetDragImage() and PathCleanupSpec().
[wine] / dlls / shell32 / contmenu.c
1 /*
2  *      IContextMenu
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
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
20 DEFAULT_DEBUG_CHANNEL(shell)
21
22 /**************************************************************************
23 *  IContextMenu Implementation
24 */
25 typedef struct 
26 {       ICOM_VTABLE(IContextMenu)* lpvtbl;
27         DWORD           ref;
28         IShellFolder*   pSFParent;
29         LPITEMIDLIST    pidl;           /* root pidl */
30         LPITEMIDLIST    *aPidls;        /* array of child pidls */
31         BOOL            bAllValues;
32 } IContextMenuImpl;
33
34
35 static struct ICOM_VTABLE(IContextMenu) cmvt;
36
37 /**************************************************************************
38 *  IContextMenu_AllocPidlTable()
39 */
40 BOOL IContextMenu_AllocPidlTable(IContextMenuImpl *This, DWORD dwEntries)
41 {
42         TRACE("(%p)->(entrys=%lu)\n",This, dwEntries);
43
44         /*add one for NULL terminator */
45         dwEntries++;
46
47         This->aPidls = (LPITEMIDLIST*)SHAlloc(dwEntries * sizeof(LPITEMIDLIST));
48
49         if(This->aPidls)
50         { ZeroMemory(This->aPidls, dwEntries * sizeof(LPITEMIDLIST));   /*set all of the entries to NULL*/
51         }
52         return (This->aPidls != NULL);
53 }
54
55 /**************************************************************************
56 * IContextMenu_FreePidlTable()
57 */
58 void IContextMenu_FreePidlTable(IContextMenuImpl *This)
59 {
60         int   i;
61
62         TRACE("(%p)->()\n",This);
63
64         if(This->aPidls)
65         { for(i = 0; This->aPidls[i]; i++)
66           { SHFree(This->aPidls[i]);
67           }
68
69           SHFree(This->aPidls);
70           This->aPidls = NULL;
71         }
72 }
73
74 /**************************************************************************
75 * IContextMenu_FillPidlTable()
76 */
77 BOOL IContextMenu_FillPidlTable(IContextMenuImpl *This, LPCITEMIDLIST *aPidls, UINT uItemCount)
78 {
79         UINT  i;
80
81         TRACE("(%p)->(apidl=%p count=%u)\n",This, aPidls, uItemCount);
82
83         if(This->aPidls)
84         { for(i = 0; i < uItemCount; i++)
85           { This->aPidls[i] = ILClone(aPidls[i]);
86           }
87           return TRUE;
88         }
89         return FALSE;
90 }
91
92 /**************************************************************************
93 * IContextMenu_CanRenameItems()
94 */
95 BOOL IContextMenu_CanRenameItems(IContextMenuImpl *This)
96 {       UINT  i;
97         DWORD dwAttributes;
98
99         TRACE("(%p)->()\n",This);
100
101         if(This->aPidls)
102         {
103           for(i = 0; This->aPidls[i]; i++){} /*get the number of items assigned to This object*/
104           { if(i > 1)   /*you can't rename more than one item at a time*/
105             { return FALSE;
106             }
107           }
108           dwAttributes = SFGAO_CANRENAME;
109           IShellFolder_GetAttributesOf(This->pSFParent, i, (LPCITEMIDLIST*)This->aPidls, &dwAttributes);
110
111           return dwAttributes & SFGAO_CANRENAME;
112         }
113         return FALSE;
114 }
115
116 /**************************************************************************
117 *   IContextMenu_Constructor()
118 */
119 IContextMenu *IContextMenu_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST pidl, LPCITEMIDLIST *aPidls, UINT uItemCount)
120 {       IContextMenuImpl* cm;
121         UINT  u;
122
123         cm = (IContextMenuImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IContextMenuImpl));
124         cm->lpvtbl=&cmvt;
125         cm->ref = 1;
126         cm->pidl = ILClone(pidl);
127
128         cm->pSFParent = pSFParent;
129
130         if(pSFParent)
131            IShellFolder_AddRef(pSFParent);
132
133         cm->aPidls = NULL;
134
135         IContextMenu_AllocPidlTable(cm, uItemCount);
136
137         if(cm->aPidls)
138         { IContextMenu_FillPidlTable(cm, aPidls, uItemCount);
139         }
140
141         cm->bAllValues = 1;
142         for(u = 0; u < uItemCount; u++)
143         { cm->bAllValues &= (_ILIsValue(aPidls[u]) ? 1 : 0);
144         }
145         TRACE("(%p)->()\n",cm);
146         shell32_ObjCount++;
147         return (IContextMenu*)cm;
148 }
149
150 /**************************************************************************
151 *  IContextMenu_fnQueryInterface
152 */
153 static HRESULT WINAPI IContextMenu_fnQueryInterface(IContextMenu *iface, REFIID riid, LPVOID *ppvObj)
154 {
155         ICOM_THIS(IContextMenuImpl, iface);
156
157         char    xriid[50];
158         WINE_StringFromCLSID((LPCLSID)riid,xriid);
159
160         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,xriid,ppvObj);
161
162         *ppvObj = NULL;
163
164         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
165         { *ppvObj = This; 
166         }
167         else if(IsEqualIID(riid, &IID_IContextMenu))  /*IContextMenu*/
168         { *ppvObj = This;
169         }   
170         else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
171         { FIXME("-- LPSHELLEXTINIT pointer requested\n");
172         }
173
174         if(*ppvObj)
175         { 
176           IContextMenu_AddRef((IContextMenu*)*ppvObj);      
177           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
178           return S_OK;
179         }
180         TRACE("-- Interface: E_NOINTERFACE\n");
181         return E_NOINTERFACE;
182 }
183
184 /**************************************************************************
185 *  IContextMenu_fnAddRef
186 */
187 static ULONG WINAPI IContextMenu_fnAddRef(IContextMenu *iface)
188 {
189         ICOM_THIS(IContextMenuImpl, iface);
190
191         TRACE("(%p)->(count=%lu)\n",This, This->ref);
192
193         shell32_ObjCount++;
194         return ++(This->ref);
195 }
196
197 /**************************************************************************
198 *  IContextMenu_fnRelease
199 */
200 static ULONG WINAPI IContextMenu_fnRelease(IContextMenu *iface)
201 {
202         ICOM_THIS(IContextMenuImpl, iface);
203
204         TRACE("(%p)->()\n",This);
205
206         shell32_ObjCount--;
207
208         if (!--(This->ref)) 
209         { TRACE(" destroying IContextMenu(%p)\n",This);
210
211           if(This->pSFParent)
212             IShellFolder_Release(This->pSFParent);
213
214           if(This->pidl)
215             SHFree(This->pidl);
216           
217           /*make sure the pidl is freed*/
218           if(This->aPidls)
219           { IContextMenu_FreePidlTable(This);
220           }
221
222           HeapFree(GetProcessHeap(),0,This);
223           return 0;
224         }
225         return This->ref;
226 }
227
228 /**************************************************************************
229 *  ICM_InsertItem()
230 */ 
231 void WINAPI _InsertMenuItem (
232         HMENU hmenu,
233         UINT indexMenu,
234         BOOL fByPosition,
235         UINT wID,
236         UINT fType,
237         LPSTR dwTypeData,
238         UINT fState)
239 {
240         MENUITEMINFOA   mii;
241
242         ZeroMemory(&mii, sizeof(mii));
243         mii.cbSize = sizeof(mii);
244         if (fType == MFT_SEPARATOR)
245         { mii.fMask = MIIM_ID | MIIM_TYPE;
246         }
247         else
248         { mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
249           mii.dwTypeData = dwTypeData;
250           mii.fState = fState;
251         }
252         mii.wID = wID;
253         mii.fType = fType;
254         InsertMenuItemA( hmenu, indexMenu, fByPosition, &mii);
255 }
256 /**************************************************************************
257 * IContextMenu_fnQueryContextMenu()
258 */
259
260 static HRESULT WINAPI IContextMenu_fnQueryContextMenu(
261         IContextMenu *iface,
262         HMENU hmenu,
263         UINT indexMenu,
264         UINT idCmdFirst,
265         UINT idCmdLast,
266         UINT uFlags)
267 {
268         ICOM_THIS(IContextMenuImpl, iface);
269
270         BOOL    fExplore ;
271
272         TRACE("(%p)->(hmenu=%x indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
273
274         if(!(CMF_DEFAULTONLY & uFlags))
275         { if(!This->bAllValues) 
276           { /* folder menu */
277             fExplore = uFlags & CMF_EXPLORE;
278             if(fExplore) 
279             { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED|MFS_DEFAULT);
280               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, "&Open", MFS_ENABLED);
281             }
282             else
283             { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, "&Open", MFS_ENABLED|MFS_DEFAULT);
284               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_EXPLORE, MFT_STRING, "&Explore", MFS_ENABLED);
285             }
286
287             if(uFlags & CMF_CANRENAME)
288             { _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
289               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_RENAME, MFT_STRING, "&Rename", (IContextMenu_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED));
290             }
291           }
292           else  /* file menu */
293           { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, "&Open", MFS_ENABLED|MFS_DEFAULT);
294             if(uFlags & CMF_CANRENAME)
295             { _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
296               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_RENAME, MFT_STRING, "&Rename", (IContextMenu_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED));
297             }
298           }
299           return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (IDM_LAST + 1));
300         }
301         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
302 }
303
304 /**************************************************************************
305 * IContextMenu_fnInvokeCommand()
306 */
307 static HRESULT WINAPI IContextMenu_fnInvokeCommand(
308         IContextMenu *iface,
309         LPCMINVOKECOMMANDINFO lpcmi)
310 {
311         ICOM_THIS(IContextMenuImpl, iface);
312
313         LPITEMIDLIST    pidlFQ;
314         LPSHELLBROWSER  lpSB;
315         LPSHELLVIEW     lpSV;
316         HWND    hWndSV;
317         SHELLEXECUTEINFOA       sei;
318         int   i;
319
320         TRACE("(%p)->(invcom=%p verb=%p wnd=%x)\n",This,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);    
321
322         if(LOWORD(lpcmi->lpVerb) > IDM_LAST)
323           return E_INVALIDARG;
324
325         switch(LOWORD(lpcmi->lpVerb))
326         { case IDM_EXPLORE:
327           case IDM_OPEN:
328           /* Find the first item in the list that is not a value. These commands 
329             should never be invoked if there isn't at least one folder item in the list.*/
330
331             for(i = 0; This->aPidls[i]; i++)
332             { if(!_ILIsValue(This->aPidls[i]))
333                  break;
334             }
335
336             pidlFQ = ILCombine(This->pidl, This->aPidls[i]);
337
338             ZeroMemory(&sei, sizeof(sei));
339             sei.cbSize = sizeof(sei);
340             sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
341             sei.lpIDList = pidlFQ;
342             sei.lpClass = "folder";
343             sei.hwnd = lpcmi->hwnd;
344             sei.nShow = SW_SHOWNORMAL;
345
346             if(LOWORD(lpcmi->lpVerb) == IDM_EXPLORE)
347             { sei.lpVerb = "explore";
348             }
349             else
350             { sei.lpVerb = "open";
351             }
352             ShellExecuteExA(&sei);
353             SHFree(pidlFQ);
354             break;
355                 
356           case IDM_RENAME:
357             MessageBeep(MB_OK);
358             /*handle rename for the view here*/
359             break;          
360         }
361         return NOERROR;
362 }
363
364 /**************************************************************************
365 *  IContextMenu_fnGetCommandString()
366 */
367 static HRESULT WINAPI IContextMenu_fnGetCommandString(
368         IContextMenu *iface,
369         UINT idCommand,
370         UINT uFlags,
371         LPUINT lpReserved,
372         LPSTR lpszName,
373         UINT uMaxNameLen)
374 {       
375         ICOM_THIS(IContextMenuImpl, iface);
376
377         HRESULT  hr = E_INVALIDARG;
378
379         TRACE("(%p)->(idcom=%x flags=%x %p name=%p len=%x)\n",This, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
380
381         switch(uFlags)
382         { case GCS_HELPTEXT:
383             hr = E_NOTIMPL;
384             break;
385
386           case GCS_VERBA:
387             switch(idCommand)
388             { case IDM_RENAME:
389                 strcpy((LPSTR)lpszName, "rename");
390                 hr = NOERROR;
391                 break;
392             }
393             break;
394
395              /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This 
396              case, you need to do the lstrcpyW to the pointer passed.*/
397           case GCS_VERBW:
398             switch(idCommand)
399             { case IDM_RENAME:
400                 lstrcpyAtoW((LPWSTR)lpszName, "rename");
401                 hr = NOERROR;
402                 break;
403             }
404             break;
405
406           case GCS_VALIDATE:
407             hr = NOERROR;
408             break;
409         }
410         TRACE("-- (%p)->(name=%s)\n",This, lpszName);
411         return hr;
412 }
413
414 /**************************************************************************
415 * IContextMenu_fnHandleMenuMsg()
416 * NOTES
417 *  should be only in IContextMenu2 and IContextMenu3
418 *  is nevertheless called from word95
419 */
420 static HRESULT WINAPI IContextMenu_fnHandleMenuMsg(
421         IContextMenu *iface,
422         UINT uMsg,
423         WPARAM wParam,
424         LPARAM lParam)
425 {
426         ICOM_THIS(IContextMenuImpl, iface);
427
428         TRACE("(%p)->(msg=%x wp=%x lp=%lx)\n",This, uMsg, wParam, lParam);
429
430         return E_NOTIMPL;
431 }
432
433 /**************************************************************************
434 * IContextMenu VTable
435
436 */
437 static struct ICOM_VTABLE(IContextMenu) cmvt = 
438 {       
439         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
440         IContextMenu_fnQueryInterface,
441         IContextMenu_fnAddRef,
442         IContextMenu_fnRelease,
443         IContextMenu_fnQueryContextMenu,
444         IContextMenu_fnInvokeCommand,
445         IContextMenu_fnGetCommandString,
446         IContextMenu_fnHandleMenuMsg,
447         (void *) 0xdeadbabe     /* just paranoia */
448 };
449