Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
14 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: I C O N T E X T M . C P P
  7. //
  8. // Contents: IContextMenu implementation for CUPnPDeviceFolderExtractIcon
  9. //
  10. // Notes:
  11. //
  12. // Author: jeffspr 24 Oct 1997
  13. //
  14. //----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #pragma hdrstop
  17. #include "upsres.h"
  18. #include "oncommand.h"
  19. #include "cmdtable.h"
  20. struct ContextMenuEntry
  21. {
  22. INT iMenu;
  23. INT iVerbMenu;
  24. INT iDefaultCmd;
  25. };
  26. static const ContextMenuEntry c_CMEArray[] =
  27. {
  28. // standard (no menu deviances yet)
  29. { MENU_STANDARD, MENU_STANDARD_V, CMIDM_INVOKE },
  30. };
  31. const DWORD g_dwContextMenuEntryCount = celems(c_CMEArray);
  32. //+---------------------------------------------------------------------------
  33. //
  34. // Member: CUPnPDeviceFolderContextMenu::CreateInstance
  35. //
  36. // Purpose: Create an instance of the CUPnPDeviceFolderContextMenu object
  37. //
  38. // Arguments:
  39. // riid [in] Interface requested
  40. // ppv [out] Pointer to receive the requested interface
  41. //
  42. // Returns:
  43. //
  44. // Author: jeffspr 7 Aug 1998
  45. //
  46. // Notes:
  47. //
  48. HRESULT CUPnPDeviceFolderContextMenu::CreateInstance(
  49. REFIID riid,
  50. void** ppv,
  51. CMENU_TYPE cmt,
  52. HWND hwndOwner,
  53. UINT cidl,
  54. LPCITEMIDLIST * apidl,
  55. LPSHELLFOLDER psf)
  56. {
  57. HRESULT hr = E_OUTOFMEMORY;
  58. CUPnPDeviceFolderContextMenu * pObj = NULL;
  59. TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::CreateInstance");
  60. pObj = new CComObject <CUPnPDeviceFolderContextMenu>;
  61. if (pObj)
  62. {
  63. // Do the standard CComCreator::CreateInstance stuff.
  64. //
  65. pObj->SetVoid (NULL);
  66. pObj->InternalFinalConstructAddRef ();
  67. hr = pObj->FinalConstruct ();
  68. pObj->InternalFinalConstructRelease ();
  69. if (SUCCEEDED(hr))
  70. {
  71. hr = pObj->HrInitialize(cmt, hwndOwner, cidl, apidl, psf);
  72. if (SUCCEEDED(hr))
  73. {
  74. hr = pObj->QueryInterface (riid, ppv);
  75. }
  76. }
  77. if (FAILED(hr))
  78. {
  79. delete pObj;
  80. }
  81. }
  82. TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::CreateInstance");
  83. return hr;
  84. }
  85. //+---------------------------------------------------------------------------
  86. //
  87. // Member: CUPnPDeviceFolderContextMenu::CUPnPDeviceFolderContextMenu
  88. //
  89. // Purpose: Constructor for CUPnPDeviceFolderContextMenu. Initialize
  90. // data members
  91. //
  92. // Arguments:
  93. // (none)
  94. //
  95. // Returns:
  96. //
  97. // Author: jeffspr 7 Aug 1998
  98. //
  99. // Notes:
  100. //
  101. CUPnPDeviceFolderContextMenu::CUPnPDeviceFolderContextMenu()
  102. {
  103. m_psf = NULL;
  104. m_cidl = 0;
  105. m_apidl = NULL;
  106. m_hwndOwner = NULL;
  107. m_cmt = CMT_OBJECT; // arbitrary. Just make sure it's something
  108. }
  109. //+---------------------------------------------------------------------------
  110. //
  111. // Member: CUPnPDeviceFolderContextMenu::HrInitialize
  112. //
  113. // Purpose: Initialization for the context menu object. Make copies of
  114. // the pidl array, etc.
  115. //
  116. // Arguments:
  117. // hwndOwner [in] Our parent hwnd
  118. // cidl [in] Count of objects
  119. // apidl [in] Pidl array of selected items
  120. // psf [in] Our shell folder pointer
  121. //
  122. // Returns:
  123. //
  124. // Author: jeffspr 7 Aug 1998
  125. //
  126. // Notes:
  127. //
  128. HRESULT CUPnPDeviceFolderContextMenu::HrInitialize(
  129. CMENU_TYPE cmt,
  130. HWND hwndOwner,
  131. UINT cidl,
  132. LPCITEMIDLIST * apidl,
  133. LPSHELLFOLDER psf)
  134. {
  135. HRESULT hr = NOERROR;
  136. // Grab and addref the folder object
  137. //
  138. Assert(psf);
  139. psf->AddRef();
  140. m_psf = static_cast<CUPnPDeviceFolder *>(psf);
  141. // Copy the context menu type (object -vs- background)
  142. //
  143. m_cmt = cmt;
  144. // Note: This will be NULL if the context menu is invoked from the desktop
  145. //
  146. m_hwndOwner = hwndOwner;
  147. if (cidl)
  148. {
  149. Assert(CMT_OBJECT == cmt);
  150. // Clone the pidl array
  151. //
  152. hr = HrCloneRgIDL(apidl, cidl, &m_apidl, &m_cidl);
  153. if (FAILED(hr))
  154. {
  155. TraceHr(ttidError, FAL, hr, FALSE, "HrCloneRgIDL failed on apidl in "
  156. "CUPnPDeviceFolderContextMenu::HrInitialize");
  157. }
  158. }
  159. else
  160. {
  161. Assert(CMT_BACKGROUND == cmt);
  162. }
  163. TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::HrInitialize");
  164. return hr;
  165. }
  166. //+---------------------------------------------------------------------------
  167. //
  168. // Member: CUPnPDeviceFolderContextMenu::~CUPnPDeviceFolderContextMenu
  169. //
  170. // Purpose: Destructor. Free the pidl array and release the shell folder
  171. // object
  172. //
  173. // Arguments:
  174. // (none)
  175. //
  176. // Returns:
  177. //
  178. // Author: jeffspr 7 Aug 1998
  179. //
  180. // Notes:
  181. //
  182. CUPnPDeviceFolderContextMenu::~CUPnPDeviceFolderContextMenu()
  183. {
  184. if (m_apidl)
  185. {
  186. FreeRgIDL(m_cidl, m_apidl);
  187. m_apidl = NULL;
  188. m_cidl = 0;
  189. }
  190. if (m_psf)
  191. {
  192. ReleaseObj(reinterpret_cast<LPSHELLFOLDER>(m_psf));
  193. }
  194. }
  195. //+---------------------------------------------------------------------------
  196. //
  197. // Member: CUPnPDeviceFolderContextMenu::QueryContextMenu
  198. //
  199. // Purpose: Adds menu items to the specified menu. The menu items should
  200. // be inserted in the menu at the position specified by
  201. // indexMenu, and their menu item identifiers must be between
  202. // the idCmdFirst and idCmdLast parameter values.
  203. //
  204. // Arguments:
  205. // hmenu [in] Handle to the menu. The handler should specify this
  206. // handle when adding menu items
  207. // indexMenu [in] Zero-based position at which to insert the first
  208. // menu item.
  209. // idCmdFirst [in] Min value the handler can specify for a menu item
  210. // idCmdLast [in] Max value the handler can specify for a menu item
  211. // uFlags [in] Optional flags specifying how the context menu
  212. // can be changed. See MSDN for the full list.
  213. //
  214. // Returns:
  215. //
  216. // Author: jeffspr 7 Aug 1998
  217. //
  218. // Notes:
  219. //
  220. HRESULT CUPnPDeviceFolderContextMenu::QueryContextMenu(
  221. HMENU hmenu,
  222. UINT indexMenu,
  223. UINT idCmdFirst,
  224. UINT idCmdLast,
  225. UINT uFlags)
  226. {
  227. HRESULT hr = NOERROR;
  228. QCMINFO qcm = {hmenu, indexMenu, idCmdFirst, idCmdLast};
  229. INT iDefaultCmd = 0;
  230. BOOL fValidMenu = FALSE;
  231. BOOL fVerbsOnly = !!(uFlags & CMF_VERBSONLY);
  232. INT iMenuResourceId = 0;
  233. INT iPopupResourceId = 0;
  234. TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::QueryContextMenu");
  235. if (!(uFlags & CMF_DVFILE))
  236. {
  237. if (CMT_OBJECT == m_cmt)
  238. {
  239. PUPNPDEVICEFOLDPIDL pudfp = NULL;
  240. if (FIsUPnPDeviceFoldPidl(m_apidl[0]))
  241. {
  242. pudfp = ConvertToUPnPDevicePIDL(m_apidl[0]);
  243. }
  244. if(pudfp)
  245. {
  246. DWORD dwLoop = 0;
  247. for (dwLoop = 0; (dwLoop < g_dwContextMenuEntryCount) && !fValidMenu; dwLoop++)
  248. {
  249. // Leave dwLoop alone, since this is how we pick the menu below
  250. fValidMenu = TRUE;
  251. if (fValidMenu)
  252. {
  253. iPopupResourceId = 0;
  254. if (fVerbsOnly)
  255. {
  256. iMenuResourceId = c_CMEArray[dwLoop].iVerbMenu;
  257. }
  258. else
  259. {
  260. iMenuResourceId = c_CMEArray[dwLoop].iMenu;
  261. }
  262. iDefaultCmd = c_CMEArray[dwLoop].iDefaultCmd;
  263. }
  264. }
  265. }
  266. }
  267. else
  268. {
  269. if (CMT_BACKGROUND == m_cmt)
  270. {
  271. AssertSz(m_hwndOwner, "Background context menu requires a valid HWND");
  272. if (m_cidl > 0)
  273. {
  274. AssertSz(FALSE, "We shouldn't be getting this interface if connections are selected");
  275. }
  276. else
  277. {
  278. fValidMenu = TRUE;
  279. iMenuResourceId = MENU_MERGE_FOLDER_BACKGROUND;
  280. iPopupResourceId = POPUP_MERGE_FOLDER_BACKGROUND;
  281. }
  282. }
  283. else
  284. {
  285. AssertSz(FALSE, "Don't support context menus for this CMENU_TYPE. Not background or object?");
  286. }
  287. }
  288. // If we have a valid menu to give to the user, then do the merge,
  289. // enable or disable as appropriate, and set the appropriate HR to
  290. // specify the number of items added.
  291. //
  292. if (fValidMenu)
  293. {
  294. MergeMenu(_Module.GetResourceInstance(),
  295. iMenuResourceId,
  296. iPopupResourceId,
  297. (LPQCMINFO)&qcm);
  298. // Enable/Disable the menu items as appropriate. Ignore the return from this
  299. // as we're getting it for debugging purposes only.
  300. //
  301. hr = HrEnableOrDisableMenuItems(
  302. m_hwndOwner,
  303. (LPCITEMIDLIST *) m_apidl,
  304. m_cidl,
  305. hmenu,
  306. idCmdFirst);
  307. if (CMT_OBJECT == m_cmt)
  308. {
  309. // $$REVIEW: Find out why I'm only doing this for CMT_OBJECT instead of for background.
  310. // Pre-icomtextm|mb combine, mb had this commented out.
  311. //
  312. SetMenuDefaultItem(hmenu, idCmdFirst + iDefaultCmd, FALSE);
  313. }
  314. hr = ResultFromShort(qcm.idCmdFirst - idCmdFirst);
  315. }
  316. }
  317. // Ignore this trace if it's a short, basically.
  318. //
  319. TraceHr(ttidError, FAL, hr, SUCCEEDED(hr), "CUPnPDeviceFolderContextMenu::QueryContextMenu");
  320. return hr;
  321. }
  322. //+---------------------------------------------------------------------------
  323. //
  324. // Member: CUPnPDeviceFolderContextMenu::InvokeCommand
  325. //
  326. // Purpose: Carries out the command associated with a context menu item.
  327. //
  328. // Arguments:
  329. // lpici [in] Address of a CMINVOKECOMMANDINFO structure containing
  330. // information about the command.
  331. //
  332. // Returns: Returns NOERROR if successful, or an OLE-defined
  333. // error code otherwise.
  334. //
  335. // Author: jeffspr 27 Apr 1999
  336. //
  337. // Notes:
  338. //
  339. HRESULT CUPnPDeviceFolderContextMenu::InvokeCommand(
  340. LPCMINVOKECOMMANDINFO lpici)
  341. {
  342. HRESULT hr = NOERROR;
  343. UINT uiCmd = 0;
  344. TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::InvokeCommand");
  345. Assert(lpici);
  346. Assert(lpici->lpVerb);
  347. if (HIWORD(lpici->lpVerb))
  348. {
  349. // Deal with string commands
  350. PSTR pszCmd = (PSTR)lpici->lpVerb;
  351. // Only folder objects currently support string-based invoke commands.
  352. // (The background does not)
  353. //
  354. if (CMT_OBJECT == m_cmt)
  355. {
  356. if (0 == lstrcmpA(pszCmd, "delete"))
  357. {
  358. uiCmd = CMIDM_DELETE;
  359. }
  360. else if (0 == lstrcmpA(pszCmd, "properties"))
  361. {
  362. uiCmd = CMIDM_PROPERTIES;
  363. }
  364. else if (0 == lstrcmpA(pszCmd, "rename"))
  365. {
  366. uiCmd = CMIDM_RENAME;
  367. }
  368. else if (0 == lstrcmpA(pszCmd, "link"))
  369. {
  370. uiCmd = CMIDM_CREATE_SHORTCUT;
  371. }
  372. }
  373. if (0 == uiCmd)
  374. {
  375. TraceTag(ttidError, "Unprocessed InvokeCommand<%s>\n", pszCmd);
  376. hr = E_INVALIDARG;
  377. }
  378. }
  379. else
  380. {
  381. uiCmd = (UINT)LOWORD((DWORD_PTR)lpici->lpVerb);
  382. }
  383. if (SUCCEEDED(hr))
  384. {
  385. // Handle the actual command
  386. //
  387. hr = HrFolderCommandHandler(uiCmd, m_apidl, m_cidl, m_hwndOwner, lpici, m_psf);
  388. }
  389. TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolderContextMenu::InvokeCommand");
  390. return hr;
  391. }
  392. HRESULT CUPnPDeviceFolderContextMenu::GetCommandString(
  393. UINT_PTR idCmd,
  394. UINT uType,
  395. UINT * pwReserved,
  396. PSTR pszName,
  397. UINT cchMax)
  398. {
  399. HRESULT hr = E_FAIL; // Not handled
  400. TraceTag(ttidShellFolderIface, "OBJ: CCFCM - IContextMenu::GetCommandString");
  401. // note(cmr): GCS_HELPTEXT is defined as GCS_HELPTEXTW on UNICODE builds
  402. // and GCS_HELPTEXTA otherwise.
  403. // When we get GCS_HELPTEXTW, we really need to return a PWSTR
  404. // in pszName (yes, even though it's defined as a PSTR).
  405. // When we get GCS_HELPTEXTA, we just return a PSTR in pszName.
  406. // GCS_VERB has the same semantics (with GCS_VERBW and GCS_VERBA)
  407. // Basically, we just treat pszName as a PTSTR, and everything
  408. // works fine.
  409. *((PTSTR)pszName) = TEXT('\0');
  410. if (uType == GCS_HELPTEXT)
  411. {
  412. int iLength = LoadString( _Module.GetResourceInstance(),
  413. idCmd + IDS_CMIDM_START,
  414. (LPTSTR)pszName,
  415. cchMax);
  416. if (iLength > 0)
  417. {
  418. hr = NOERROR;
  419. }
  420. else
  421. {
  422. AssertSz(FALSE, "Resource string not found for one of the connections folder commands");
  423. }
  424. }
  425. else
  426. {
  427. if (uType == GCS_VERB && idCmd == CMIDM_RENAME)
  428. {
  429. // "rename" is language independent
  430. _tcsncpy((LPTSTR)pszName, TEXT("rename"), cchMax);
  431. pszName[cchMax - 1] = TEXT('\0');
  432. hr = NOERROR;
  433. }
  434. }
  435. TraceHr(ttidError, FAL, hr, (hr == E_FAIL), "CUPnPDeviceFolderContextMenu::GetCommandString");
  436. return hr;
  437. }