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