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