xref: /reactos/base/shell/explorer/startctxmnu.cpp (revision 43b3280f)
1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "precomp.h"
22 
23 /*
24  * Start menu button context menu
25  */
26 
27 class CStartMenuBtnCtxMenu :
28     public CComCoClass<CStartMenuBtnCtxMenu>,
29     public CComObjectRootEx<CComMultiThreadModelNoCS>,
30     public IContextMenu
31 {
32     /* AddStartContextMenuItems uses ID_SHELL_CMD IDs directly and relies on idCmdFirst being 0.
33      * CTrayWindow::TrackCtxMenu must pass 0 because DeleteMenu ID_SHELL_CMD_UNDO_ACTION would
34      * delete the wrong item if it used 1. m_Inner->QueryContextMenu is not aware of this game
35      * so we have to reserve the entire ID_SHELL_CMD range for ourselves here. */
36     enum { INNERIDOFFSET = ID_SHELL_CMD_LAST + 1 };
IsShellCmdId(UINT_PTR id)37     static BOOL IsShellCmdId(UINT_PTR id) { return id < INNERIDOFFSET; }
38 
39     CComPtr<ITrayWindow>  m_TrayWnd;
40     CComPtr<IContextMenu> m_Inner;
41     CComPtr<IShellFolder> m_Folder;
42 
43     HWND m_Owner;
44     LPITEMIDLIST m_FolderPidl;
45 
CreateContextMenuFromShellFolderPidl(HMENU hPopup,UINT idCmdFirst,UINT idCmdLast)46     HRESULT CreateContextMenuFromShellFolderPidl(HMENU hPopup, UINT idCmdFirst, UINT idCmdLast)
47     {
48         HRESULT hRet;
49 
50         hRet = m_Folder->GetUIObjectOf(m_Owner, 1, (LPCITEMIDLIST *) &m_FolderPidl, IID_NULL_PPV_ARG(IContextMenu, &m_Inner));
51         if (SUCCEEDED(hRet))
52         {
53             if (hPopup != NULL)
54             {
55                 hRet = m_Inner->QueryContextMenu(
56                     hPopup,
57                     0,
58                     idCmdFirst,
59                     idCmdLast,
60                     CMF_VERBSONLY);
61 
62                 if (SUCCEEDED(hRet))
63                 {
64                     return hRet;
65                 }
66             }
67         }
68         return E_FAIL;
69     }
70 
AddStartContextMenuItems(IN HMENU hPopup)71     VOID AddStartContextMenuItems(IN HMENU hPopup)
72     {
73         WCHAR szBuf[MAX_PATH];
74         HRESULT hRet;
75         C_ASSERT(ID_SHELL_CMD_FIRST != 0);
76          /* If this ever asserts, let m_Inner use 1..ID_SHELL_CMD_FIRST-1 instead */
77         C_ASSERT(ID_SHELL_CMD_LAST < 0xffff / 2);
78 
79         /* Add the "Open All Users" menu item */
80         if (LoadStringW(hExplorerInstance,
81                         IDS_PROPERTIES,
82                         szBuf,
83                         _countof(szBuf)))
84         {
85             AppendMenu(hPopup,
86                        MF_STRING,
87                        ID_SHELL_CMD_PROPERTIES,
88                        szBuf);
89         }
90 
91         if (!SHRestricted(REST_NOCOMMONGROUPS))
92         {
93             /* Check if we should add menu items for the common start menu */
94             hRet = SHGetFolderPath(m_Owner,
95                                    CSIDL_COMMON_STARTMENU,
96                                    NULL,
97                                    SHGFP_TYPE_CURRENT,
98                                    szBuf);
99             if (SUCCEEDED(hRet) && hRet != S_FALSE)
100             {
101                 /* The directory exists, but only show the items if the
102                 user can actually make any changes to the common start
103                 menu. This is most likely only the case if the user
104                 has administrative rights! */
105                 if (IsUserAnAdmin())
106                 {
107                     AppendMenu(hPopup,
108                                MF_SEPARATOR,
109                                0,
110                                NULL);
111 
112                     /* Add the "Open All Users" menu item */
113                     if (LoadStringW(hExplorerInstance,
114                                     IDS_OPEN_ALL_USERS,
115                                     szBuf,
116                                     _countof(szBuf)))
117                     {
118                         AppendMenu(hPopup,
119                                    MF_STRING,
120                                    ID_SHELL_CMD_OPEN_ALL_USERS,
121                                    szBuf);
122                     }
123 
124                     /* Add the "Explore All Users" menu item */
125                     if (LoadStringW(hExplorerInstance,
126                                     IDS_EXPLORE_ALL_USERS,
127                                     szBuf,
128                                     _countof(szBuf)))
129                     {
130                         AppendMenu(hPopup,
131                                    MF_STRING,
132                                    ID_SHELL_CMD_EXPLORE_ALL_USERS,
133                                    szBuf);
134                     }
135                 }
136             }
137         }
138     }
139 
140 public:
Initialize(ITrayWindow * pTrayWnd,IN HWND hWndOwner)141     HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
142     {
143         m_TrayWnd = pTrayWnd;
144         m_Owner = hWndOwner;
145         return S_OK;
146     }
147 
148     virtual HRESULT STDMETHODCALLTYPE
QueryContextMenu(HMENU hPopup,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)149         QueryContextMenu(HMENU hPopup,
150                          UINT indexMenu,
151                          UINT idCmdFirst,
152                          UINT idCmdLast,
153                          UINT uFlags)
154     {
155         LPITEMIDLIST pidlStart;
156         CComPtr<IShellFolder> psfDesktop;
157         HRESULT hRet = S_OK;
158         UINT idInnerFirst = idCmdFirst + INNERIDOFFSET;
159 
160         psfDesktop = NULL;
161         m_Inner = NULL;
162 
163         pidlStart = SHCloneSpecialIDList(m_Owner, CSIDL_STARTMENU, TRUE);
164         if (pidlStart != NULL)
165         {
166             m_FolderPidl = ILClone(ILFindLastID(pidlStart));
167             ILRemoveLastID(pidlStart);
168 
169             if (m_FolderPidl != NULL)
170             {
171                 hRet = SHGetDesktopFolder(&psfDesktop);
172                 if (SUCCEEDED(hRet))
173                 {
174                     hRet = psfDesktop->BindToObject(pidlStart, NULL, IID_PPV_ARG(IShellFolder, &m_Folder));
175                     if (SUCCEEDED(hRet))
176                     {
177                         hRet = CreateContextMenuFromShellFolderPidl(hPopup, idInnerFirst, idCmdLast);
178                     }
179                 }
180             }
181 
182             ILFree(pidlStart);
183         }
184         if (idCmdLast - idCmdFirst >= ID_SHELL_CMD_LAST - ID_SHELL_CMD_FIRST)
185         {
186             AddStartContextMenuItems(hPopup);
187             hRet = SUCCEEDED(hRet) ? hRet + idInnerFirst : idInnerFirst;
188         }
189         return hRet;
190     }
191 
192     virtual HRESULT STDMETHODCALLTYPE
InvokeCommand(LPCMINVOKECOMMANDINFO lpici)193         InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
194     {
195         UINT uiCmdId = PtrToUlong(lpici->lpVerb);
196         if (!IsShellCmdId((UINT_PTR)lpici->lpVerb))
197         {
198             CMINVOKECOMMANDINFO cmici = { 0 };
199             CHAR szDir[MAX_PATH];
200 
201             /* Setup and invoke the shell command */
202             cmici.cbSize = sizeof(cmici);
203             cmici.hwnd = m_Owner;
204             if (IS_INTRESOURCE(lpici->lpVerb))
205                 cmici.lpVerb = MAKEINTRESOURCEA(uiCmdId - INNERIDOFFSET);
206             else
207                 cmici.lpVerb = lpici->lpVerb;
208             cmici.nShow = SW_NORMAL;
209 
210             /* FIXME: Support Unicode!!! */
211             if (SHGetPathFromIDListA(m_FolderPidl, szDir))
212             {
213                 cmici.lpDirectory = szDir;
214             }
215 
216             return m_Inner->InvokeCommand(&cmici);
217         }
218         m_TrayWnd->ExecContextMenuCmd(uiCmdId);
219         return S_OK;
220     }
221 
222     virtual HRESULT STDMETHODCALLTYPE
GetCommandString(UINT_PTR idCmd,UINT uType,UINT * pwReserved,LPSTR pszName,UINT cchMax)223         GetCommandString(UINT_PTR idCmd,
224                          UINT uType,
225                          UINT *pwReserved,
226                          LPSTR pszName,
227                          UINT cchMax)
228     {
229         if (!IsShellCmdId(idCmd) && m_Inner)
230             return m_Inner->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
231         return E_NOTIMPL;
232     }
233 
CStartMenuBtnCtxMenu()234     CStartMenuBtnCtxMenu()
235     {
236     }
237 
~CStartMenuBtnCtxMenu()238     virtual ~CStartMenuBtnCtxMenu()
239     {
240         if (m_FolderPidl)
241             ILFree(m_FolderPidl);
242     }
243 
244     BEGIN_COM_MAP(CStartMenuBtnCtxMenu)
245         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
246     END_COM_MAP()
247 };
248 
CStartMenuBtnCtxMenu_CreateInstance(ITrayWindow * m_TrayWnd,IN HWND m_Owner,IContextMenu ** ppCtxMenu)249 HRESULT CStartMenuBtnCtxMenu_CreateInstance(ITrayWindow * m_TrayWnd, IN HWND m_Owner, IContextMenu ** ppCtxMenu)
250 {
251     return ShellObjectCreatorInit<CStartMenuBtnCtxMenu>(m_TrayWnd, m_Owner, IID_PPV_ARG(IContextMenu, ppCtxMenu));
252 }
253