00001 /* Helper to create context menu for host objects and execute user choice 00002 00003 Copyright (C) 2007 Alexander Lamaison <awl03@doc.ic.ac.uk> 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License along 00016 with this program; if not, write to the Free Software Foundation, Inc., 00017 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00018 */ 00019 00020 #include "stdafx.h" 00021 #include "HostContextMenu.h" 00022 00023 /*------------------------------------------------------------------------------ 00024 * CHostContextMenu::Initialize 00025 * Initialises the context menu object for a given host connection object. 00026 * Used in place of passing parameters to a constructor due to ATL template. 00027 * The PIDL passed in is an absolute PIDL to the host connection RemoteFolder 00028 * and is needed to perform ShellExecuteEx on if the Connect item is chosen. 00029 *----------------------------------------------------------------------------*/ 00030 STDMETHODIMP CHostContextMenu::Initialize( PCIDLIST_ABSOLUTE pidl ) 00031 { 00032 ATLTRACE("CHostContextMenu::Initialize called\n"); 00033 if (pidl == NULL) 00034 return E_POINTER; 00035 00036 // TODO: use a PidlManager to verify PIDLs? 00037 00038 m_pidl = ILClone( pidl ); 00039 return S_OK; 00040 } 00041 00042 // IContextMenu 00043 00044 /*------------------------------------------------------------------------------ 00045 * CHostContextMenu::QueryContextMenu : IContextMenu 00046 * Adds items to the given context menu (hMenu). 00047 * The first position at which the item should be inserted is given in 00048 * indexMenu (TODO: when would this not be 0?). The menu command IDs should 00049 * lie between idCmdFirst and idCmdLast and are saved for later use. 00050 * We return the largest command id of the menu items created plus one. 00051 *----------------------------------------------------------------------------*/ 00052 STDMETHODIMP CHostContextMenu::QueryContextMenu( 00053 __in HMENU hMenu, __in UINT indexMenu, __in UINT idCmdFirst, 00054 __in UINT idCmdLast, __in UINT uFlags) 00055 { 00056 ATLTRACE("CHostContextMenu::QueryContextMenu called\n"); 00057 ATLASSERT(idCmdFirst + MENUOFFSET_LAST < idCmdLast); 00058 00059 // Add menu item at next position with menu command calculated from offset 00060 InsertMenu( hMenu, indexMenu++, MF_BYPOSITION, 00061 idCmdFirst + MENUOFFSET_CONNECT, _T("&Connect") ); 00062 00063 // The CMF_DEFAULTONLY flag tells namespace extensions to add only 00064 // the default menu item - we only have one at all, currently, but 00065 // when we have more we will need: if (uFlags & CMF_DEFAULTONLY) 00066 00067 // Set Connect menu verb as default 00068 if (!(uFlags & CMF_NODEFAULT)) 00069 SetMenuDefaultItem( hMenu, idCmdFirst + MENUOFFSET_CONNECT, false ); 00070 00071 // Return largest menu offset + 1 00072 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(MENUOFFSET_LAST + 1)); 00073 } 00074 00075 /*------------------------------------------------------------------------------ 00076 * CHostContextMenu::GetCommandString : IContextMenu 00077 * Language-independent verb or status bar help string requested. 00078 * The request can be for either an ANSI or a unicode version (indicated 00079 * with uType) and both should be supported. Returns S_OK or E_INVALIDARG if 00080 * idCmd is not valid for this menu. 00081 *----------------------------------------------------------------------------*/ 00082 STDMETHODIMP CHostContextMenu::GetCommandString( 00083 __in UINT_PTR idCmd, __in UINT uType, __reserved UINT *pReserved, 00084 __out_awcount(!(uType & GCS_UNICODE), cchMax) LPSTR pszName, 00085 __in UINT cchMax) 00086 { 00087 ATLTRACE("CRemoteFolder::GetCommandString called\n"); 00088 (void)pReserved; 00089 HRESULT hr = E_INVALIDARG; 00090 00091 // Validate idCmd (deals with GCS_VALIDATE into the bargain) 00092 if(idCmd < MENUOFFSET_FIRST || idCmd > MENUOFFSET_LAST) 00093 return hr; 00094 00095 // If the code reaches an ANSI GCS_ case it is most likely because the 00096 // unicode version failed and Explorer is trying ANSI as a fallback 00097 00098 switch(uType) 00099 { 00100 case GCS_HELPTEXTA: 00101 if (idCmd == MENUOFFSET_CONNECT) 00102 hr = StringCchCopyA(pszName, cchMax, 00103 "Connect to remote filesystem over SFTP"); 00104 break; 00105 00106 case GCS_HELPTEXTW: 00107 if (idCmd == MENUOFFSET_CONNECT) 00108 hr = StringCchCopyW((PWSTR)pszName, cchMax, 00109 L"Connect to remote filesystem over SFTP"); 00110 break; 00111 00112 case GCS_VERBA: 00113 if (idCmd == MENUOFFSET_CONNECT) 00114 hr = StringCchCopyA(pszName, cchMax, "connect"); 00115 break; 00116 00117 case GCS_VERBW: 00118 if (idCmd == MENUOFFSET_CONNECT) 00119 hr = StringCchCopyW((PWSTR)pszName, cchMax, L"connect"); 00120 break; 00121 00122 default: 00123 hr = S_OK; 00124 break; 00125 } 00126 return hr; 00127 } 00128 00129 /*------------------------------------------------------------------------------ 00130 * CHostContextMenu::InvokeCommand : IContextMenu 00131 * A menu command has been selected to execute on the PIDL. 00132 * The chosen command can be either an ANSI verb, a unicode VERB or a menu ID. 00133 * We parse the value passed to determing which one and then execute the 00134 * chosen command. 00135 * Currently, only the 'connect' command is supported and we simply invoke 00136 * the default action for a folder type (open). 00137 * If the command verb/id is not recognised we return E_FAIL. 00138 *----------------------------------------------------------------------------*/ 00139 STDMETHODIMP CHostContextMenu::InvokeCommand( 00140 __in CMINVOKECOMMANDINFO *pici ) 00141 { 00142 ATLTRACE("CRemoteFolder::InvokeCommand called\n"); 00143 00144 BOOL fUnicode = FALSE; // Is CMINVOKECOMMANDINFO struct unicode? 00145 MENUOFFSET menuCmd = MENUOFFSET_NULL; 00146 // Store chosen command from parsing 00147 CMINVOKECOMMANDINFOEX *piciEx = { 0 }; 00148 // pici cast to unicode version (if needed) 00149 00150 if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX) && 00151 (pici->fMask & CMIC_MASK_UNICODE)) 00152 { 00153 // Given command info struct is actually a unicode version, 00154 // cast accordingly 00155 fUnicode = TRUE; 00156 piciEx = (CMINVOKECOMMANDINFOEX *) pici; 00157 } 00158 00159 if (!fUnicode && HIWORD(pici->lpVerb)) // ANSI 00160 { 00161 if (!StrCmpIA( pici->lpVerb, "connect" )) 00162 menuCmd = MENUOFFSET_CONNECT; 00163 } 00164 else if(fUnicode && HIWORD(piciEx->lpVerbW)) // Unicode 00165 { 00166 if(StrCmpIW( piciEx->lpVerbW, L"connect" )) 00167 menuCmd = MENUOFFSET_CONNECT; 00168 } 00169 else if(LOWORD(pici->lpVerb) == MENUOFFSET_CONNECT) // Menu ID 00170 { 00171 menuCmd = MENUOFFSET_CONNECT; 00172 } 00173 else // Invalid verb/ID 00174 { 00175 // An attempt was made to invoke a verb/ID not supported by 00176 // this menu's PIDL. For the time being, we are assuming this 00177 // doesn't happen and all cases are caught by one of the above. 00178 UNREACHABLE; 00179 return E_FAIL; 00180 } 00181 00182 ATLASSERT(menuCmd >= MENUOFFSET_FIRST && menuCmd <= MENUOFFSET_LAST); 00183 00184 // Set up ShellExecute to execute chosen verb on this menu's PIDL (m_pidl) 00185 SHELLEXECUTEINFO sei; 00186 ZeroMemory(&sei, sizeof(sei)); 00187 sei.cbSize = sizeof(sei); 00188 // Needed if multiple menu items used 00189 // if (menuCmd == MENUOFFSET_CONNECT) 00190 // sei.lpVerb = _T("connect"); 00191 sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME; 00192 sei.lpIDList = ILClone(m_pidl); 00193 sei.lpClass = _T("folder"); 00194 sei.hwnd = pici->hwnd; 00195 sei.nShow = SW_SHOWNORMAL; 00196 00197 HRESULT hr = (ShellExecuteEx(&sei)) ? S_OK : E_FAIL; 00198 00199 ILFree((LPITEMIDLIST) sei.lpIDList); 00200 00201 return hr; 00202 } 00203 00204 // CHostContextMenu 00205