1 /* 2 * PROJECT: shell32 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/win32/shell32/CDefViewBckgrndMenu.cpp 5 * PURPOSE: background context menu of the CDefView 6 * PROGRAMMERS: Giannis Adamopoulos 7 */ 8 9 #include <precomp.h> 10 11 WINE_DEFAULT_DEBUG_CHANNEL(shell); 12 13 class CDefViewBckgrndMenu : 14 public CComObjectRootEx<CComMultiThreadModelNoCS>, 15 public IContextMenu3, 16 public IObjectWithSite 17 { 18 private: 19 CComPtr<IUnknown> m_site; 20 CComPtr<IShellFolder> m_psf; 21 CComPtr<IContextMenu> m_folderCM; 22 23 UINT m_idCmdFirst; 24 UINT m_LastFolderCMId; 25 26 BOOL _bIsDesktopBrowserMenu(); 27 BOOL _bCanPaste(); 28 public: 29 CDefViewBckgrndMenu(); 30 ~CDefViewBckgrndMenu(); 31 HRESULT Initialize(IShellFolder* psf); 32 33 // IContextMenu 34 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); 35 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi); 36 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen); 37 38 // IContextMenu2 39 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam); 40 41 // IContextMenu3 42 virtual HRESULT WINAPI HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult); 43 44 // IObjectWithSite 45 virtual HRESULT STDMETHODCALLTYPE SetSite(IUnknown *pUnkSite); 46 virtual HRESULT STDMETHODCALLTYPE GetSite(REFIID riid, void **ppvSite); 47 48 BEGIN_COM_MAP(CDefViewBckgrndMenu) 49 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) 50 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2) 51 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3) 52 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) 53 END_COM_MAP() 54 }; 55 56 CDefViewBckgrndMenu::CDefViewBckgrndMenu() 57 { 58 m_idCmdFirst = 0; 59 m_LastFolderCMId = 0; 60 } 61 62 CDefViewBckgrndMenu::~CDefViewBckgrndMenu() 63 { 64 } 65 66 BOOL CDefViewBckgrndMenu::_bIsDesktopBrowserMenu() 67 { 68 if (!m_site) 69 return FALSE; 70 71 /* Get a pointer to the shell browser */ 72 CComPtr<IShellView> psv; 73 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv)); 74 if (FAILED_UNEXPECTEDLY(hr)) 75 return FALSE; 76 77 FOLDERSETTINGS FolderSettings; 78 hr = psv->GetCurrentInfo(&FolderSettings); 79 if (FAILED_UNEXPECTEDLY(hr)) 80 return FALSE; 81 82 return ((FolderSettings.fFlags & FWF_DESKTOP) == FWF_DESKTOP); 83 } 84 85 BOOL CDefViewBckgrndMenu::_bCanPaste() 86 { 87 /* If the folder doesn't have a drop target we can't paste */ 88 CComPtr<IDropTarget> pdt; 89 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdt)); 90 if (FAILED(hr)) 91 return FALSE; 92 93 /* We can only paste if CFSTR_SHELLIDLIST is present in the clipboard */ 94 CComPtr<IDataObject> pDataObj; 95 hr = OleGetClipboard(&pDataObj); 96 if (FAILED(hr)) 97 return FALSE; 98 99 STGMEDIUM medium; 100 FORMATETC formatetc; 101 102 /* Set the FORMATETC structure*/ 103 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL); 104 hr = pDataObj->GetData(&formatetc, &medium); 105 if (FAILED(hr)) 106 return FALSE; 107 108 ReleaseStgMedium(&medium); 109 return TRUE; 110 } 111 112 HRESULT 113 CDefViewBckgrndMenu::Initialize(IShellFolder* psf) 114 { 115 m_psf = psf; 116 117 /* Get the context menu of the folder. Do it here because someone may call 118 InvokeCommand without calling QueryContextMenu. It is fine if this fails */ 119 m_psf->CreateViewObject(NULL, IID_PPV_ARG(IContextMenu, &m_folderCM)); 120 121 return S_OK; 122 } 123 124 HRESULT 125 WINAPI 126 CDefViewBckgrndMenu::SetSite(IUnknown *pUnkSite) 127 { 128 m_site = pUnkSite; 129 130 if(m_folderCM) 131 IUnknown_SetSite(m_folderCM, pUnkSite); 132 133 return S_OK; 134 } 135 136 HRESULT 137 WINAPI 138 CDefViewBckgrndMenu::GetSite(REFIID riid, void **ppvSite) 139 { 140 if (!m_site) 141 return E_FAIL; 142 143 return m_site->QueryInterface(riid, ppvSite); 144 } 145 146 HRESULT 147 WINAPI 148 CDefViewBckgrndMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 149 { 150 HRESULT hr; 151 HMENU hMenuPart; 152 UINT cIds = 0; 153 154 /* This is something the implementations of IContextMenu should never really do. 155 However CDefViewBckgrndMenu is more or less an overengineering result, its code could really be part of the 156 CDefView. Given this, I think that abusing the interface here is not that bad since only CDefView is the ony 157 user of this class. Here we need to do two things to keep things as simple as possible. 158 First we want the menu part added by the shell folder to be the first to add so as to make as few id translations 159 as possible. Second, we want to add the default part of the background menu without shifted ids, so as 160 to let the CDefView fill some parts like filling the arrange modes or checking the view mode. In order 161 for that to work we need to save idCmdFirst because our caller will pass id offsets to InvokeCommand. 162 This makes it impossible to concatenate the CDefViewBckgrndMenu with other menus since it abuses IContextMenu 163 but as stated above, its sole user is CDefView and should really be that way. */ 164 m_idCmdFirst = idCmdFirst; 165 166 /* Let the shell folder add any items it wants to add in the background context menu */ 167 if (m_folderCM) 168 { 169 hr = m_folderCM->QueryContextMenu(hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags); 170 if (SUCCEEDED(hr)) 171 { 172 m_LastFolderCMId = LOWORD(hr); 173 cIds = m_LastFolderCMId; 174 } 175 else 176 { 177 WARN("QueryContextMenu failed!\n"); 178 } 179 } 180 else 181 { 182 WARN("GetUIObjectOf didn't give any context menu!\n"); 183 } 184 185 /* Load the default part of the background context menu */ 186 hMenuPart = LoadMenuW(shell32_hInstance, L"MENU_002"); 187 if (hMenuPart) 188 { 189 /* Don't show the view submenu for the desktop */ 190 if (_bIsDesktopBrowserMenu()) 191 { 192 DeleteMenu(hMenuPart, FCIDM_SHVIEW_VIEW, MF_BYCOMMAND); 193 } 194 195 /* Disable the paste options if it is not possible */ 196 if (!_bCanPaste()) 197 { 198 EnableMenuItem(hMenuPart, FCIDM_SHVIEW_INSERT, MF_BYCOMMAND | MF_GRAYED); 199 EnableMenuItem(hMenuPart, FCIDM_SHVIEW_INSERTLINK, MF_BYCOMMAND | MF_GRAYED); 200 } 201 202 /* merge general background context menu in */ 203 Shell_MergeMenus(hMenu, GetSubMenu(hMenuPart, 0), indexMenu, 0, idCmdLast, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR); 204 DestroyMenu(hMenuPart); 205 } 206 else 207 { 208 ERR("Failed to load menu from resource!\n"); 209 } 210 211 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds); 212 } 213 214 HRESULT 215 WINAPI 216 CDefViewBckgrndMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 217 { 218 UINT idCmd = LOWORD(lpcmi->lpVerb); 219 220 if (HIWORD(lpcmi->lpVerb) && !strcmp(lpcmi->lpVerb, CMDSTR_VIEWLISTA)) 221 { 222 idCmd = FCIDM_SHVIEW_LISTVIEW; 223 } 224 else if (HIWORD(lpcmi->lpVerb) && !strcmp(lpcmi->lpVerb, CMDSTR_VIEWDETAILSA)) 225 { 226 idCmd = FCIDM_SHVIEW_REPORTVIEW; 227 } 228 else if(HIWORD(lpcmi->lpVerb) != 0 || idCmd < m_LastFolderCMId) 229 { 230 if (m_folderCM) 231 { 232 return m_folderCM->InvokeCommand(lpcmi); 233 } 234 WARN("m_folderCM is NULL!\n"); 235 return E_NOTIMPL; 236 } 237 else 238 { 239 /* The default part of the background menu doesn't have shifted ids so we need to convert the id offset to the real id */ 240 idCmd += m_idCmdFirst; 241 } 242 243 /* The commands that are handled by the def view are forwarded to it */ 244 switch (idCmd) 245 { 246 case FCIDM_SHVIEW_INSERT: 247 case FCIDM_SHVIEW_INSERTLINK: 248 if (m_folderCM) 249 { 250 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd); 251 return m_folderCM->InvokeCommand(lpcmi); 252 } 253 WARN("m_folderCM is NULL!\n"); 254 return E_NOTIMPL; 255 case FCIDM_SHVIEW_BIGICON: 256 case FCIDM_SHVIEW_SMALLICON: 257 case FCIDM_SHVIEW_LISTVIEW: 258 case FCIDM_SHVIEW_REPORTVIEW: 259 case 0x30: /* FIX IDS in resource files */ 260 case 0x31: 261 case 0x32: 262 case 0x33: 263 case FCIDM_SHVIEW_AUTOARRANGE: 264 case FCIDM_SHVIEW_SNAPTOGRID: 265 case FCIDM_SHVIEW_REFRESH: 266 case FCIDM_SHVIEW_ALIGNTOGRID: 267 if (!m_site) 268 return E_FAIL; 269 270 /* Get a pointer to the shell browser */ 271 CComPtr<IShellView> psv; 272 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv)); 273 if (FAILED_UNEXPECTEDLY(hr)) 274 return hr; 275 276 HWND hwndSV = NULL; 277 if (SUCCEEDED(psv->GetWindow(&hwndSV))) 278 SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(idCmd, 0), 0); 279 return S_OK; 280 } 281 282 ERR("Got unknown command id %ul\n", LOWORD(lpcmi->lpVerb)); 283 return E_FAIL; 284 } 285 286 HRESULT 287 WINAPI 288 CDefViewBckgrndMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) 289 { 290 if (m_folderCM) 291 { 292 return m_folderCM->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); 293 } 294 295 return E_NOTIMPL; 296 } 297 298 HRESULT 299 WINAPI 300 CDefViewBckgrndMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam) 301 { 302 if(m_folderCM) 303 { 304 CComPtr<IContextMenu2> pfolderCM2; 305 HRESULT hr = m_folderCM->QueryInterface(IID_PPV_ARG(IContextMenu2, &pfolderCM2)); 306 if (FAILED_UNEXPECTEDLY(hr)) 307 return hr; 308 309 return pfolderCM2->HandleMenuMsg(uMsg, wParam, lParam); 310 } 311 312 return E_NOTIMPL; 313 } 314 315 HRESULT 316 WINAPI 317 CDefViewBckgrndMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) 318 { 319 if(m_folderCM) 320 { 321 CComPtr<IContextMenu3> pfolderCM3; 322 HRESULT hr = m_folderCM->QueryInterface(IID_PPV_ARG(IContextMenu3, &pfolderCM3)); 323 if (FAILED_UNEXPECTEDLY(hr)) 324 return hr; 325 326 return pfolderCM3->HandleMenuMsg2(uMsg, wParam, lParam, plResult); 327 } 328 329 return E_NOTIMPL; 330 } 331 332 333 HRESULT 334 CDefViewBckgrndMenu_CreateInstance(IShellFolder* psf, REFIID riid, void **ppv) 335 { 336 return ShellObjectCreatorInit<CDefViewBckgrndMenu>(psf, riid, ppv); 337 } 338