Many fixes.
[wine] / dlls / shell32 / contmenu.c
1 /*
2  *      IContextMenu
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
5  */
6 #include "windows.h"
7 #include "winerror.h"
8 #include "debug.h"
9 #include "pidl.h"
10 #include "shlobj.h"
11 #include "shell32_main.h"
12 #include "shresdef.h"
13 #include "if_macros.h"
14
15 #define __T(x)      x
16 #define _T(x)       __T(x)
17 #define TEXT        _T
18
19 static HRESULT WINAPI IContextMenu_QueryInterface(LPCONTEXTMENU ,REFIID , LPVOID *);
20 static ULONG WINAPI IContextMenu_AddRef(LPCONTEXTMENU);
21 static ULONG WINAPI IContextMenu_Release(LPCONTEXTMENU);
22 static HRESULT WINAPI IContextMenu_QueryContextMenu(LPCONTEXTMENU , HMENU32 ,UINT32 ,UINT32 ,UINT32 ,UINT32);
23 static HRESULT WINAPI IContextMenu_InvokeCommand(LPCONTEXTMENU, LPCMINVOKECOMMANDINFO32);
24 static HRESULT WINAPI IContextMenu_GetCommandString(LPCONTEXTMENU , UINT32 ,UINT32 ,LPUINT32 ,LPSTR ,UINT32);
25 static HRESULT WINAPI IContextMenu_HandleMenuMsg(LPCONTEXTMENU, UINT32, WPARAM32, LPARAM);
26
27 BOOL32 IContextMenu_AllocPidlTable(LPCONTEXTMENU, DWORD);
28 void IContextMenu_FreePidlTable(LPCONTEXTMENU);
29 BOOL32 IContextMenu_CanRenameItems(LPCONTEXTMENU);
30 BOOL32 IContextMenu_FillPidlTable(LPCONTEXTMENU, LPCITEMIDLIST *, UINT32);
31
32 static struct IContextMenu_VTable cmvt = 
33 {       IContextMenu_QueryInterface,
34         IContextMenu_AddRef,
35         IContextMenu_Release,
36         IContextMenu_QueryContextMenu,
37         IContextMenu_InvokeCommand,
38         IContextMenu_GetCommandString,
39         IContextMenu_HandleMenuMsg,
40         (void *) 0xdeadbabe     /* just paranoia */
41 };
42 /**************************************************************************
43 *  IContextMenu_QueryInterface
44 */
45 static HRESULT WINAPI IContextMenu_QueryInterface(LPCONTEXTMENU this,REFIID riid, LPVOID *ppvObj)
46 { char    xriid[50];
47   WINE_StringFromCLSID((LPCLSID)riid,xriid);
48   TRACE(shell,"(%p)->(\n\tIID:\t%s,%p)\n",this,xriid,ppvObj);
49
50   *ppvObj = NULL;
51
52   if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
53   { *ppvObj = (LPUNKNOWN)(LPCONTEXTMENU)this; 
54   }
55   else if(IsEqualIID(riid, &IID_IContextMenu))  /*IContextMenu*/
56   { *ppvObj = (LPCONTEXTMENU)this;
57   }   
58   else if(IsEqualIID(riid, &IID_IShellExtInit))  /*IShellExtInit*/
59   { *ppvObj = (LPSHELLEXTINIT)this;
60     WARN(shell,"-- LPSHELLEXTINIT pointer requested\n");  
61   }   
62
63   if(*ppvObj)
64   { (*(LPCONTEXTMENU *)ppvObj)->lpvtbl->fnAddRef(this);      
65     TRACE(shell,"-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
66     return S_OK;
67   }
68   TRACE(shell,"-- Interface: E_NOINTERFACE\n");
69   return E_NOINTERFACE;
70 }   
71
72 /**************************************************************************
73 *  IContextMenu_AddRef
74 */
75 static ULONG WINAPI IContextMenu_AddRef(LPCONTEXTMENU this)
76 { TRACE(shell,"(%p)->(count=%lu)\n",this,(this->ref)+1);
77   return ++(this->ref);
78 }
79 /**************************************************************************
80 *  IContextMenu_Release
81 */
82 static ULONG WINAPI IContextMenu_Release(LPCONTEXTMENU this)
83 {       TRACE(shell,"(%p)->()\n",this);
84         if (!--(this->ref)) 
85         { TRACE(shell," destroying IContextMenu(%p)\n",this);
86
87           if(this->pSFParent)
88           this->pSFParent->lpvtbl->fnRelease(this->pSFParent);
89
90           /*make sure the pidl is freed*/
91           if(this->aPidls)
92           { IContextMenu_FreePidlTable(this);
93           }
94
95           HeapFree(GetProcessHeap(),0,this);
96           return 0;
97         }
98         return this->ref;
99 }
100
101 /**************************************************************************
102 *   IContextMenu_Constructor()
103 */
104 LPCONTEXTMENU IContextMenu_Constructor(LPSHELLFOLDER pSFParent, LPCITEMIDLIST *aPidls, UINT32 uItemCount)
105 {       LPCONTEXTMENU cm;
106         UINT32  u;
107     
108         cm = (LPCONTEXTMENU)HeapAlloc(GetProcessHeap(),0,sizeof(IContextMenu));
109         cm->lpvtbl=&cmvt;
110         cm->ref = 1;
111
112         cm->pSFParent = pSFParent;
113         if(cm->pSFParent)
114            cm->pSFParent->lpvtbl->fnAddRef(cm->pSFParent);
115
116         cm->aPidls = NULL;
117
118         IContextMenu_AllocPidlTable(cm, uItemCount);
119     
120         if(cm->aPidls)
121         { IContextMenu_FillPidlTable(cm, aPidls, uItemCount);
122         }
123
124         cm->bAllValues = 1;
125         for(u = 0; u < uItemCount; u++)
126         { cm->bAllValues &= (_ILIsValue(aPidls[u]) ? 1 : 0);
127         }
128         TRACE(shell,"(%p)->()\n",cm);
129         return cm;
130 }
131 /**************************************************************************
132 *  ICM_InsertItem()
133 */ 
134 void WINAPI _InsertMenuItem (HMENU32 hmenu, UINT32 indexMenu, BOOL32 fByPosition, 
135                         UINT32 wID, UINT32 fType, LPSTR dwTypeData, UINT32 fState)
136 {       MENUITEMINFO32A mii;
137
138         ZeroMemory(&mii, sizeof(mii));
139         mii.cbSize = sizeof(mii);
140         if (fType == MFT_SEPARATOR)
141         { mii.fMask = MIIM_ID | MIIM_TYPE;
142         }
143         else
144         { mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
145           mii.dwTypeData = dwTypeData;
146           mii.fState = MFS_ENABLED | MFS_DEFAULT;
147         }
148         mii.wID = wID;
149         mii.fType = fType;
150         InsertMenuItem32A( hmenu, indexMenu, fByPosition, &mii);
151 }
152 /**************************************************************************
153 * IContextMenu_QueryContextMenu()
154 */
155
156 static HRESULT WINAPI IContextMenu_QueryContextMenu( LPCONTEXTMENU this, HMENU32 hmenu, UINT32 indexMenu,
157                                                         UINT32 idCmdFirst,UINT32 idCmdLast,UINT32 uFlags)
158 {       BOOL32  fExplore ;
159
160         TRACE(shell,"(%p)->(hmenu=%x indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",this, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
161
162         if(!(CMF_DEFAULTONLY & uFlags))
163         { if(!this->bAllValues) 
164           { /* folder menu */
165             fExplore = uFlags & CMF_EXPLORE;
166             if(fExplore) 
167             { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_EXPLORE, MFT_STRING, TEXT("&Explore"), MFS_ENABLED|MFS_DEFAULT);
168               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, TEXT("&Open"), MFS_ENABLED);
169             }
170             else
171             { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, TEXT("&Open"), MFS_ENABLED|MFS_DEFAULT);
172               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_EXPLORE, MFT_STRING, TEXT("&Explore"), MFS_ENABLED);
173             }
174
175             if(uFlags & CMF_CANRENAME)
176             { _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
177               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_RENAME, MFT_STRING, TEXT("&Rename"), (IContextMenu_CanRenameItems(this) ? MFS_ENABLED : MFS_DISABLED));
178             }
179           }
180           else  /* file menu */
181           { _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_OPEN, MFT_STRING, TEXT("&Open"), MFS_ENABLED|MFS_DEFAULT);
182             if(uFlags & CMF_CANRENAME)
183             { _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
184               _InsertMenuItem(hmenu, indexMenu++, TRUE, idCmdFirst+IDM_RENAME, MFT_STRING, TEXT("&Rename"), (IContextMenu_CanRenameItems(this) ? MFS_ENABLED : MFS_DISABLED));
185             }
186           }
187           return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (IDM_LAST + 1));
188         }
189         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
190 }
191
192 /**************************************************************************
193 * IContextMenu_InvokeCommand()
194 */
195 static HRESULT WINAPI IContextMenu_InvokeCommand(LPCONTEXTMENU this, LPCMINVOKECOMMANDINFO32 lpcmi)
196 {       LPITEMIDLIST    pidlTemp,pidlFQ;
197         LPSHELLBROWSER  lpSB;
198         LPSHELLVIEW     lpSV;
199         HWND32  hWndSV;
200         SHELLEXECUTEINFO32A     sei;
201         int   i;
202
203         TRACE(shell,"(%p)->(invcom=%p verb=%p wnd=%x)\n",this,lpcmi,lpcmi->lpVerb, lpcmi->hwnd);    
204
205         if(HIWORD(lpcmi->lpVerb))
206         { /* get the active IShellView */
207           lpSB = (LPSHELLBROWSER)SendMessage32A(lpcmi->hwnd, CWM_GETISHELLBROWSER,0,0);
208           IShellBrowser_QueryActiveShellView(lpSB, &lpSV);
209           lpSV->lpvtbl->fnGetWindow(lpSV, &hWndSV);
210           
211           /* these verbs are used by the filedialogs*/
212           if (! strcmp(lpcmi->lpVerb,CMDSTR_NEWFOLDER))
213           { FIXME(shell,"%s\n",lpcmi->lpVerb);
214           }
215           else if (! strcmp(lpcmi->lpVerb,CMDSTR_VIEWLIST))
216           { FIXME(shell,"%s\n",lpcmi->lpVerb);
217             SendMessage32A(hWndSV, WM_COMMAND, MAKEWPARAM(FCIDM_SHVIEW_LISTVIEW,0),0 );
218           }
219           else if (! strcmp(lpcmi->lpVerb,CMDSTR_VIEWDETAILS))
220           { FIXME(shell,"%s\n",lpcmi->lpVerb);
221             SendMessage32A(hWndSV, WM_COMMAND, MAKEWPARAM(FCIDM_SHVIEW_REPORTVIEW,0),0 );
222           } 
223           else
224           { FIXME(shell,"please report: unknown verb %s\n",lpcmi->lpVerb);
225           }
226           return NOERROR;
227         }
228
229         if(LOWORD(lpcmi->lpVerb) > IDM_LAST)
230           return E_INVALIDARG;
231
232         switch(LOWORD(lpcmi->lpVerb))
233         { case IDM_EXPLORE:
234           case IDM_OPEN:
235             /* Find the first item in the list that is not a value. These commands 
236             should never be invoked if there isn't at least one folder item in the list.*/
237
238             for(i = 0; this->aPidls[i]; i++)
239             { if(!_ILIsValue(this->aPidls[i]))
240                 break;
241             }
242       
243             pidlTemp = ILCombine(this->pSFParent->mpidl, this->aPidls[i]);
244             pidlFQ = ILCombine(this->pSFParent->pMyPidl, pidlTemp);
245             SHFree(pidlTemp);
246       
247             ZeroMemory(&sei, sizeof(sei));
248             sei.cbSize = sizeof(sei);
249             sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
250             sei.lpIDList = pidlFQ;
251             sei.lpClass = TEXT("folder");
252             sei.hwnd = lpcmi->hwnd;
253             sei.nShow = SW_SHOWNORMAL;
254       
255             if(LOWORD(lpcmi->lpVerb) == IDM_EXPLORE)
256             { sei.lpVerb = TEXT("explore");
257             }
258             else
259             { sei.lpVerb = TEXT("open");
260             }
261             ShellExecuteEx32A(&sei);
262             SHFree(pidlFQ);
263             break;
264                 
265           case IDM_RENAME:
266             MessageBeep32(MB_OK);
267             /*handle rename for the view here*/
268             break;          
269         }
270         return NOERROR;
271 }
272
273 /**************************************************************************
274 *  IContextMenu_GetCommandString()
275 */
276 static HRESULT WINAPI IContextMenu_GetCommandString( LPCONTEXTMENU this, UINT32 idCommand,
277                                                 UINT32 uFlags,LPUINT32 lpReserved,LPSTR lpszName,UINT32 uMaxNameLen)
278 {       HRESULT  hr = E_INVALIDARG;
279
280         TRACE(shell,"(%p)->(idcom=%x flags=%x %p name=%p len=%x)\n",this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
281
282         switch(uFlags)
283         { case GCS_HELPTEXT:
284             hr = E_NOTIMPL;
285             break;
286    
287           case GCS_VERBA:
288             switch(idCommand)
289             { case IDM_RENAME:
290                 strcpy((LPSTR)lpszName, "rename");
291                 hr = NOERROR;
292                 break;
293             }
294             break;
295
296              /* NT 4.0 with IE 3.0x or no IE will always call this with GCS_VERBW. In this 
297              case, you need to do the lstrcpyW to the pointer passed.*/
298           case GCS_VERBW:
299             switch(idCommand)
300             { case IDM_RENAME:
301                 lstrcpyAtoW((LPWSTR)lpszName, "rename");
302                 hr = NOERROR;
303                 break;
304             }
305             break;
306
307           case GCS_VALIDATE:
308             hr = NOERROR;
309             break;
310         }
311         TRACE(shell,"-- (%p)->(name=%s)\n",this, lpszName);
312         return hr;
313 }
314 /**************************************************************************
315 * IContextMenu_HandleMenuMsg()
316 * NOTES
317 *  should be only in IContextMenu2 and IContextMenu3
318 *  is nevertheless called from word95
319 */
320 static HRESULT WINAPI IContextMenu_HandleMenuMsg(LPCONTEXTMENU this, UINT32 uMsg,WPARAM32 wParam,LPARAM lParam)
321 {       TRACE(shell,"(%p)->(msg=%x wp=%x lp=%lx)\n",this, uMsg, wParam, lParam);
322         return E_NOTIMPL;
323 }
324 /**************************************************************************
325 *  IContextMenu_AllocPidlTable()
326 */
327 BOOL32 IContextMenu_AllocPidlTable(LPCONTEXTMENU this, DWORD dwEntries)
328 {       //add one for NULL terminator
329         TRACE(shell,"(%p)->(entrys=%lu)\n",this, dwEntries);
330         dwEntries++;
331
332         this->aPidls = (LPITEMIDLIST*)SHAlloc(dwEntries * sizeof(LPITEMIDLIST));
333
334         if(this->aPidls)
335         { ZeroMemory(this->aPidls, dwEntries * sizeof(LPITEMIDLIST));   /*set all of the entries to NULL*/
336         }
337         return (this->aPidls != NULL);
338 }
339
340 /**************************************************************************
341 * IContextMenu_FreePidlTable()
342 */
343 void IContextMenu_FreePidlTable(LPCONTEXTMENU this)
344 {       int   i;
345
346         TRACE(shell,"(%p)->()\n",this);
347
348         if(this->aPidls)
349         { for(i = 0; this->aPidls[i]; i++)
350           { SHFree(this->aPidls[i]);
351           }
352    
353           SHFree(this->aPidls);
354           this->aPidls = NULL;
355         }
356 }
357
358 /**************************************************************************
359 * IContextMenu_FillPidlTable()
360 */
361 BOOL32 IContextMenu_FillPidlTable(LPCONTEXTMENU this, LPCITEMIDLIST *aPidls, UINT32 uItemCount)
362 {   UINT32  i;
363         TRACE(shell,"(%p)->(apidl=%p count=%u)\n",this, aPidls, uItemCount);
364         if(this->aPidls)
365         { for(i = 0; i < uItemCount; i++)
366           { this->aPidls[i] = ILClone(aPidls[i]);
367           }
368           return TRUE;
369         }
370         return FALSE;
371 }
372
373 /**************************************************************************
374 * IContextMenu_CanRenameItems()
375 */
376 BOOL32 IContextMenu_CanRenameItems(LPCONTEXTMENU this)
377 {       UINT32  i;
378         DWORD dwAttributes;
379
380         TRACE(shell,"(%p)->()\n",this);
381
382         if(this->aPidls)
383         { for(i = 0; this->aPidls[i]; i++){} /*get the number of items assigned to this object*/
384             if(i > 1)   /*you can't rename more than one item at a time*/
385             { return FALSE;
386             }
387             dwAttributes = SFGAO_CANRENAME;
388             this->pSFParent->lpvtbl->fnGetAttributesOf(this->pSFParent, i,
389                                                          (LPCITEMIDLIST*)this->aPidls, &dwAttributes);
390       
391             return dwAttributes & SFGAO_CANRENAME;
392         }
393         return FALSE;
394 }
395