2 * IContextMenu for items in the shellview
4 * Copyright 1998, 2000 Juergen Schmied <juergen.schmied@debitel.net>
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
28 #include "wine/debug.h"
33 #include "undocshell.h"
38 #include "shell32_main.h"
39 #include "shellfolder.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
48 IContextMenu2 IContextMenu2_iface
;
54 /**************************************************************************
55 * IContextMenu Implementation
61 LPITEMIDLIST pidl
; /* root pidl */
62 LPITEMIDLIST
*apidl
; /* array of child pidls */
67 static inline ItemCmImpl
*impl_from_IContextMenu2(IContextMenu2
*iface
)
69 return CONTAINING_RECORD(iface
, ItemCmImpl
, menu
.IContextMenu2_iface
);
72 /**************************************************************************
73 * ISvItemCm_CanRenameItems()
75 static BOOL
ISvItemCm_CanRenameItems(ItemCmImpl
*This
)
79 TRACE("(%p)\n", This
);
81 /* can't rename more than one item at a time*/
82 if (!This
->apidl
|| This
->cidl
> 1) return FALSE
;
84 attr
= SFGAO_CANRENAME
;
85 IShellFolder_GetAttributesOf(This
->menu
.parent
, 1, (LPCITEMIDLIST
*)This
->apidl
, &attr
);
86 return attr
& SFGAO_CANRENAME
;
89 /**************************************************************************
90 * ISvItemCm_fnQueryInterface
92 static HRESULT WINAPI
ISvItemCm_fnQueryInterface(IContextMenu2
*iface
, REFIID riid
, LPVOID
*ppvObj
)
94 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
96 TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This
,debugstr_guid(riid
),ppvObj
);
100 if(IsEqualIID(riid
, &IID_IUnknown
) ||
101 IsEqualIID(riid
, &IID_IContextMenu
) ||
102 IsEqualIID(riid
, &IID_IContextMenu2
))
106 else if(IsEqualIID(riid
, &IID_IShellExtInit
)) /*IShellExtInit*/
108 FIXME("-- LPSHELLEXTINIT pointer requested\n");
113 IUnknown_AddRef((IUnknown
*)*ppvObj
);
114 TRACE("-- Interface: (%p)->(%p)\n",ppvObj
,*ppvObj
);
117 TRACE("-- Interface: E_NOINTERFACE\n");
118 return E_NOINTERFACE
;
121 /**************************************************************************
124 static ULONG WINAPI
ISvItemCm_fnAddRef(IContextMenu2
*iface
)
126 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
127 ULONG refCount
= InterlockedIncrement(&This
->menu
.ref
);
129 TRACE("(%p)->(count=%u)\n", This
, refCount
- 1);
134 /**************************************************************************
135 * ISvItemCm_fnRelease
137 static ULONG WINAPI
ISvItemCm_fnRelease(IContextMenu2
*iface
)
139 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
140 ULONG refCount
= InterlockedDecrement(&This
->menu
.ref
);
142 TRACE("(%p)->(count=%i)\n", This
, refCount
+ 1);
146 TRACE(" destroying IContextMenu(%p)\n",This
);
148 if(This
->menu
.parent
)
149 IShellFolder_Release(This
->menu
.parent
);
153 /*make sure the pidl is freed*/
154 _ILFreeaPidl(This
->apidl
, This
->cidl
);
156 HeapFree(GetProcessHeap(),0,This
);
161 static void _InsertMenuItemW (
172 mii
.cbSize
= sizeof(mii
);
173 if (fType
== MFT_SEPARATOR
)
175 mii
.fMask
= MIIM_ID
| MIIM_TYPE
;
179 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
;
180 mii
.dwTypeData
= dwTypeData
;
185 InsertMenuItemW( hmenu
, indexMenu
, fByPosition
, &mii
);
188 /**************************************************************************
189 * ISvItemCm_fnQueryContextMenu()
191 static HRESULT WINAPI
ISvItemCm_fnQueryContextMenu(
192 IContextMenu2
*iface
,
199 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
202 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This
, hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
204 This
->menu
.verb_offset
=idCmdFirst
;
206 if(!(CMF_DEFAULTONLY
& uFlags
) && This
->cidl
>0)
208 HMENU hmenures
= LoadMenuW(shell32_hInstance
, MAKEINTRESOURCEW(MENU_SHV_FILE
));
210 if(uFlags
& CMF_EXPLORE
)
211 RemoveMenu(hmenures
, FCIDM_SHVIEW_OPEN
, MF_BYCOMMAND
);
213 uIDMax
= Shell_MergeMenus(hmenu
, GetSubMenu(hmenures
, 0), indexMenu
, idCmdFirst
, idCmdLast
, MM_SUBMENUSHAVEIDS
);
215 DestroyMenu(hmenures
);
221 mi
.cbSize
= sizeof(mi
);
222 mi
.fMask
= MIIM_ID
| MIIM_STRING
| MIIM_FTYPE
;
225 GetMenuItemInfoW(hmenu
, FCIDM_SHVIEW_EXPLORE
, MF_BYCOMMAND
, &mi
);
226 RemoveMenu(hmenu
, FCIDM_SHVIEW_EXPLORE
, MF_BYCOMMAND
);
227 _InsertMenuItemW(hmenu
, (uFlags
& CMF_EXPLORE
) ? 1 : 2, MF_BYPOSITION
, FCIDM_SHVIEW_EXPLORE
, MFT_STRING
, str
, MFS_ENABLED
);
230 SetMenuDefaultItem(hmenu
, 0, MF_BYPOSITION
);
232 if(uFlags
& ~CMF_CANRENAME
)
233 RemoveMenu(hmenu
, FCIDM_SHVIEW_RENAME
, MF_BYCOMMAND
);
235 EnableMenuItem(hmenu
, FCIDM_SHVIEW_RENAME
, MF_BYCOMMAND
| ISvItemCm_CanRenameItems(This
) ? MFS_ENABLED
: MFS_DISABLED
);
237 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, uIDMax
-idCmdFirst
);
239 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, 0);
242 /**************************************************************************
248 static void DoOpenExplore(ItemCmImpl
*This
, HWND hwnd
, LPCSTR verb
)
250 UINT i
, bFolderFound
= FALSE
;
252 SHELLEXECUTEINFOA sei
;
254 /* Find the first item in the list that is not a value. These commands
255 should never be invoked if there isn't at least one folder item in the list.*/
257 for(i
= 0; i
<This
->cidl
; i
++)
259 if(!_ILIsValue(This
->apidl
[i
]))
266 if (!bFolderFound
) return;
268 pidlFQ
= ILCombine(This
->pidl
, This
->apidl
[i
]);
270 ZeroMemory(&sei
, sizeof(sei
));
271 sei
.cbSize
= sizeof(sei
);
272 sei
.fMask
= SEE_MASK_IDLIST
| SEE_MASK_CLASSNAME
;
273 sei
.lpIDList
= pidlFQ
;
274 sei
.lpClass
= "Folder";
276 sei
.nShow
= SW_SHOWNORMAL
;
278 ShellExecuteExA(&sei
);
282 /**************************************************************************
285 static void DoRename(ItemCmImpl
*This
, HWND hwnd
)
290 TRACE("(%p)->(wnd=%p)\n",This
, hwnd
);
292 /* get the active IShellView */
293 if ((lpSB
= (LPSHELLBROWSER
)SendMessageA(hwnd
, CWM_GETISHELLBROWSER
,0,0)))
295 if(SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB
, &lpSV
)))
297 TRACE("(sv=%p)\n",lpSV
);
298 IShellView_SelectItem(lpSV
, This
->apidl
[0],
299 SVSI_DESELECTOTHERS
|SVSI_EDIT
|SVSI_ENSUREVISIBLE
|SVSI_FOCUSED
|SVSI_SELECT
);
300 IShellView_Release(lpSV
);
305 /**************************************************************************
308 * deletes the currently selected items
310 static void DoDelete(ItemCmImpl
*This
)
314 IShellFolder_QueryInterface(This
->menu
.parent
, &IID_ISFHelper
, (LPVOID
*)&psfhlp
);
317 ISFHelper_DeleteItems(psfhlp
, This
->cidl
, (LPCITEMIDLIST
*)This
->apidl
);
318 ISFHelper_Release(psfhlp
);
322 /**************************************************************************
325 * copies the currently selected items into the clipboard
327 static BOOL
DoCopyOrCut(ItemCmImpl
*This
, HWND hwnd
, BOOL bCut
)
333 TRACE("(%p)->(wnd=%p,bCut=0x%08x)\n",This
, hwnd
, bCut
);
335 /* get the active IShellView */
336 if ((lpSB
= (LPSHELLBROWSER
)SendMessageA(hwnd
, CWM_GETISHELLBROWSER
,0,0)))
338 if (SUCCEEDED(IShellBrowser_QueryActiveShellView(lpSB
, &lpSV
)))
340 if (SUCCEEDED(IShellView_GetItemObject(lpSV
, SVGIO_SELECTION
, &IID_IDataObject
, (LPVOID
*)&lpDo
)))
342 OleSetClipboard(lpDo
);
343 IDataObject_Release(lpDo
);
345 IShellView_Release(lpSV
);
351 /**************************************************************************
352 * Properties_AddPropSheetCallback
354 * Used by DoOpenProperties through SHCreatePropSheetExtArrayEx to add
355 * propertysheet pages from shell extensions.
357 static BOOL CALLBACK
Properties_AddPropSheetCallback(HPROPSHEETPAGE hpage
, LPARAM lparam
)
359 LPPROPSHEETHEADERW psh
= (LPPROPSHEETHEADERW
) lparam
;
360 psh
->u3
.phpage
[psh
->nPages
++] = hpage
;
365 /**************************************************************************
368 static void DoOpenProperties(ItemCmImpl
*This
, HWND hwnd
)
370 static const UINT MAX_PROP_PAGES
= 99;
371 static const WCHAR wszFolder
[] = {'F','o','l','d','e','r', 0};
372 static const WCHAR wszFiletypeAll
[] = {'*',0};
373 LPSHELLFOLDER lpDesktopSF
;
376 WCHAR wszFiletype
[MAX_PATH
];
377 WCHAR wszFilename
[MAX_PATH
];
378 PROPSHEETHEADERW psh
;
379 HPROPSHEETPAGE hpages
[MAX_PROP_PAGES
];
383 TRACE("(%p)->(wnd=%p)\n", This
, hwnd
);
385 ZeroMemory(&psh
, sizeof(PROPSHEETHEADERW
));
386 psh
.dwSize
= sizeof (PROPSHEETHEADERW
);
387 psh
.hwndParent
= hwnd
;
388 psh
.dwFlags
= PSH_PROPTITLE
;
390 psh
.u3
.phpage
= hpages
;
391 psh
.u2
.nStartPage
= 0;
393 _ILSimpleGetTextW(This
->apidl
[0], (LPVOID
)wszFilename
, MAX_PATH
);
394 psh
.pszCaption
= (LPCWSTR
)wszFilename
;
396 /* Find out where to look for the shell extensions */
397 if (_ILIsValue(This
->apidl
[0]))
401 if (_ILGetExtension(This
->apidl
[0], sTemp
, 64))
403 HCR_MapTypeToValueA(sTemp
, sTemp
, 64, TRUE
);
404 MultiByteToWideChar(CP_ACP
, 0, sTemp
, -1, wszFiletype
, MAX_PATH
);
411 else if (_ILIsFolder(This
->apidl
[0]))
413 lstrcpynW(wszFiletype
, wszFolder
, 64);
415 else if (_ILIsSpecialFolder(This
->apidl
[0]))
418 static const WCHAR wszclsid
[] = {'C','L','S','I','D','\\', 0};
419 folderGUID
= _ILGetGUIDPointer(This
->apidl
[0]);
420 lstrcpyW(wszFiletype
, wszclsid
);
421 StringFromGUID2(folderGUID
, &wszFiletype
[6], MAX_PATH
- 6);
425 FIXME("Requested properties for unknown type.\n");
429 /* Get a suitable DataObject for accessing the files */
430 SHGetDesktopFolder(&lpDesktopSF
);
431 if (_ILIsPidlSimple(This
->pidl
))
433 ret
= IShellFolder_GetUIObjectOf(lpDesktopSF
, hwnd
, This
->cidl
, (LPCITEMIDLIST
*)This
->apidl
,
434 &IID_IDataObject
, NULL
, (LPVOID
*)&lpDo
);
435 IShellFolder_Release(lpDesktopSF
);
439 IShellFolder_BindToObject(lpDesktopSF
, This
->pidl
, NULL
, &IID_IShellFolder
, (LPVOID
*) &lpSF
);
440 ret
= IShellFolder_GetUIObjectOf(lpSF
, hwnd
, This
->cidl
, (LPCITEMIDLIST
*)This
->apidl
,
441 &IID_IDataObject
, NULL
, (LPVOID
*)&lpDo
);
442 IShellFolder_Release(lpSF
);
443 IShellFolder_Release(lpDesktopSF
);
448 hpsxa
= SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT
, wszFiletype
, MAX_PROP_PAGES
- psh
.nPages
, lpDo
);
451 SHAddFromPropSheetExtArray(hpsxa
, Properties_AddPropSheetCallback
, (LPARAM
)&psh
);
452 SHDestroyPropSheetExtArray(hpsxa
);
454 hpsxa
= SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT
, wszFiletypeAll
, MAX_PROP_PAGES
- psh
.nPages
, lpDo
);
457 SHAddFromPropSheetExtArray(hpsxa
, Properties_AddPropSheetCallback
, (LPARAM
)&psh
);
458 SHDestroyPropSheetExtArray(hpsxa
);
460 IDataObject_Release(lpDo
);
464 PropertySheetW(&psh
);
466 FIXME("No property pages found.\n");
469 /**************************************************************************
470 * ISvItemCm_fnInvokeCommand()
472 static HRESULT WINAPI
ISvItemCm_fnInvokeCommand(
473 IContextMenu2
*iface
,
474 LPCMINVOKECOMMANDINFO lpcmi
)
476 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
478 if (lpcmi
->cbSize
!= sizeof(CMINVOKECOMMANDINFO
))
479 FIXME("Is an EX structure\n");
481 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n",This
,lpcmi
,lpcmi
->lpVerb
, lpcmi
->hwnd
);
483 if( HIWORD(lpcmi
->lpVerb
)==0 && LOWORD(lpcmi
->lpVerb
) > FCIDM_SHVIEWLAST
)
485 TRACE("Invalid Verb %x\n",LOWORD(lpcmi
->lpVerb
));
489 if (HIWORD(lpcmi
->lpVerb
) == 0)
491 switch(LOWORD(lpcmi
->lpVerb
-This
->menu
.verb_offset
))
493 case FCIDM_SHVIEW_EXPLORE
:
494 TRACE("Verb FCIDM_SHVIEW_EXPLORE\n");
495 DoOpenExplore(This
, lpcmi
->hwnd
, "explore");
497 case FCIDM_SHVIEW_OPEN
:
498 TRACE("Verb FCIDM_SHVIEW_OPEN\n");
499 DoOpenExplore(This
, lpcmi
->hwnd
, "open");
501 case FCIDM_SHVIEW_RENAME
:
502 TRACE("Verb FCIDM_SHVIEW_RENAME\n");
503 DoRename(This
, lpcmi
->hwnd
);
505 case FCIDM_SHVIEW_DELETE
:
506 TRACE("Verb FCIDM_SHVIEW_DELETE\n");
509 case FCIDM_SHVIEW_COPY
:
510 TRACE("Verb FCIDM_SHVIEW_COPY\n");
511 DoCopyOrCut(This
, lpcmi
->hwnd
, FALSE
);
513 case FCIDM_SHVIEW_CUT
:
514 TRACE("Verb FCIDM_SHVIEW_CUT\n");
515 DoCopyOrCut(This
, lpcmi
->hwnd
, TRUE
);
517 case FCIDM_SHVIEW_PROPERTIES
:
518 TRACE("Verb FCIDM_SHVIEW_PROPERTIES\n");
519 DoOpenProperties(This
, lpcmi
->hwnd
);
522 FIXME("Unhandled Verb %xl\n",LOWORD(lpcmi
->lpVerb
)-This
->menu
.verb_offset
);
528 TRACE("Verb is %s\n",debugstr_a(lpcmi
->lpVerb
));
529 if (strcmp(lpcmi
->lpVerb
,"delete")==0)
531 else if (strcmp(lpcmi
->lpVerb
,"properties")==0)
532 DoOpenProperties(This
, lpcmi
->hwnd
);
534 FIXME("Unhandled string verb %s\n",debugstr_a(lpcmi
->lpVerb
));
541 /**************************************************************************
542 * ISvItemCm_fnGetCommandString()
544 static HRESULT WINAPI
ISvItemCm_fnGetCommandString(
545 IContextMenu2
*iface
,
552 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
554 HRESULT hr
= E_INVALIDARG
;
556 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n",This
, idCommand
, uFlags
, lpReserved
, lpszName
, uMaxNameLen
);
566 switch(idCommand
-This
->menu
.verb_offset
)
568 case FCIDM_SHVIEW_RENAME
:
569 strcpy(lpszName
, "rename");
575 /* NT 4.0 with IE 3.0x or no IE will always call This with GCS_VERBW. In This
576 case, you need to do the lstrcpyW to the pointer passed.*/
578 switch(idCommand
-This
->menu
.verb_offset
)
580 case FCIDM_SHVIEW_RENAME
:
581 MultiByteToWideChar( CP_ACP
, 0, "rename", -1, (LPWSTR
)lpszName
, uMaxNameLen
);
592 TRACE("-- (%p)->(name=%s)\n",This
, lpszName
);
596 /**************************************************************************
597 * ISvItemCm_fnHandleMenuMsg()
599 * should be only in IContextMenu2 and IContextMenu3
600 * is nevertheless called from word95
602 static HRESULT WINAPI
ISvItemCm_fnHandleMenuMsg(
603 IContextMenu2
*iface
,
608 ItemCmImpl
*This
= impl_from_IContextMenu2(iface
);
610 TRACE("(%p)->(msg=%x wp=%lx lp=%lx)\n",This
, uMsg
, wParam
, lParam
);
615 static const IContextMenu2Vtbl ItemContextMenuVtbl
=
617 ISvItemCm_fnQueryInterface
,
620 ISvItemCm_fnQueryContextMenu
,
621 ISvItemCm_fnInvokeCommand
,
622 ISvItemCm_fnGetCommandString
,
623 ISvItemCm_fnHandleMenuMsg
626 IContextMenu2
*ISvItemCm_Constructor(IShellFolder
*parent
, LPCITEMIDLIST pidl
, const LPCITEMIDLIST
*apidl
, UINT cidl
)
630 cm
= HeapAlloc(GetProcessHeap(), 0, sizeof(ItemCmImpl
));
631 cm
->menu
.IContextMenu2_iface
.lpVtbl
= &ItemContextMenuVtbl
;
633 cm
->menu
.verb_offset
= 0;
634 cm
->menu
.parent
= parent
;
636 cm
->pidl
= ILClone(pidl
);
638 if (parent
) IShellFolder_AddRef(parent
);
640 cm
->apidl
= _ILCopyaPidl(apidl
, cidl
);
644 for(u
= 0; u
< cidl
; u
++)
645 cm
->bAllValues
&= (_ILIsValue(apidl
[u
]) ? 1 : 0);
649 return &cm
->menu
.IContextMenu2_iface
;