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.

459 lines
11 KiB

  1. //*******************************************************************************************
  2. //
  3. // Filename : Menu.cpp
  4. //
  5. // Implementations for CCabItemMenu methods
  6. //
  7. // Copyright (c) 1994 - 1996 Microsoft Corporation. All rights reserved
  8. //
  9. //*******************************************************************************************
  10. #include "pch.h"
  11. #include "thisdll.h"
  12. #include "resource.h"
  13. #include "folder.h"
  14. #include "menu.h"
  15. #include "dataobj.h"
  16. #include "cabitms.h"
  17. // Copy a menu onto the beginning or end of another menu
  18. // Adds uIDAdjust to each menu ID (pass in 0 for no adjustment)
  19. // Will not add any item whose adjusted ID is greater than uMaxIDAdjust
  20. // (pass in 0xffff to allow everything)
  21. // Returns one more than the maximum adjusted ID that is used
  22. //
  23. BOOL _SHIsMenuSeparator(HMENU hm, int i)
  24. {
  25. MENUITEMINFO mii;
  26. mii.cbSize = sizeof(MENUITEMINFO);
  27. mii.fMask = MIIM_TYPE;
  28. mii.cch = 0; // WARNING: We MUST initialize it to 0!!!
  29. if (!GetMenuItemInfo(hm, i, TRUE, &mii))
  30. {
  31. return(FALSE);
  32. }
  33. if (mii.fType & MFT_SEPARATOR)
  34. {
  35. return(TRUE);
  36. }
  37. return(FALSE);
  38. }
  39. //===================================================================
  40. // Cab_MergeMenu parameter
  41. //
  42. #define MM_ADDSEPARATOR 0x00000001L
  43. #define MM_SUBMENUSHAVEIDS 0x00000002L
  44. UINT Cab_MergeMenus(HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags)
  45. {
  46. int nItem;
  47. HMENU hmSubMenu;
  48. BOOL bAlreadySeparated;
  49. MENUITEMINFO miiSrc;
  50. TCHAR szName[256];
  51. UINT uTemp, uIDMax = uIDAdjust;
  52. if (!hmDst || !hmSrc)
  53. {
  54. goto MM_Exit;
  55. }
  56. nItem = GetMenuItemCount(hmDst);
  57. if (uInsert >= (UINT)nItem)
  58. {
  59. uInsert = (UINT)nItem;
  60. bAlreadySeparated = TRUE;
  61. }
  62. else
  63. {
  64. bAlreadySeparated = _SHIsMenuSeparator(hmDst, uInsert);;
  65. }
  66. if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
  67. {
  68. // Add a separator between the menus
  69. InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  70. bAlreadySeparated = TRUE;
  71. }
  72. // Go through the menu items and clone them
  73. for (nItem = GetMenuItemCount(hmSrc) - 1; nItem >= 0; nItem--)
  74. {
  75. miiSrc.cbSize = sizeof(MENUITEMINFO);
  76. miiSrc.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_TYPE | MIIM_DATA;
  77. // We need to reset this every time through the loop in case
  78. // menus DON'T have IDs
  79. miiSrc.fType = MFT_STRING;
  80. miiSrc.dwTypeData = szName;
  81. miiSrc.dwItemData = 0;
  82. miiSrc.cch = ARRAYSIZE(szName);
  83. if (!GetMenuItemInfo(hmSrc, nItem, TRUE, &miiSrc))
  84. {
  85. continue;
  86. }
  87. if (miiSrc.fType & MFT_SEPARATOR)
  88. {
  89. // This is a separator; don't put two of them in a row
  90. if (bAlreadySeparated)
  91. {
  92. continue;
  93. }
  94. bAlreadySeparated = TRUE;
  95. }
  96. else if (miiSrc.hSubMenu)
  97. {
  98. if (uFlags & MM_SUBMENUSHAVEIDS)
  99. {
  100. // Adjust the ID and check it
  101. miiSrc.wID += uIDAdjust;
  102. if (miiSrc.wID > uIDAdjustMax)
  103. {
  104. continue;
  105. }
  106. if (uIDMax <= miiSrc.wID)
  107. {
  108. uIDMax = miiSrc.wID + 1;
  109. }
  110. }
  111. else
  112. {
  113. // Don't set IDs for submenus that didn't have
  114. // them already
  115. miiSrc.fMask &= ~MIIM_ID;
  116. }
  117. hmSubMenu = miiSrc.hSubMenu;
  118. miiSrc.hSubMenu = CreatePopupMenu();
  119. if (!miiSrc.hSubMenu)
  120. {
  121. goto MM_Exit;
  122. }
  123. uTemp = Cab_MergeMenus(miiSrc.hSubMenu, hmSubMenu, 0, uIDAdjust,
  124. uIDAdjustMax, uFlags&MM_SUBMENUSHAVEIDS);
  125. if (uIDMax <= uTemp)
  126. {
  127. uIDMax = uTemp;
  128. }
  129. bAlreadySeparated = FALSE;
  130. }
  131. else
  132. {
  133. // Adjust the ID and check it
  134. miiSrc.wID += uIDAdjust;
  135. if (miiSrc.wID > uIDAdjustMax)
  136. {
  137. continue;
  138. }
  139. if (uIDMax <= miiSrc.wID)
  140. {
  141. uIDMax = miiSrc.wID + 1;
  142. }
  143. bAlreadySeparated = FALSE;
  144. }
  145. if (!InsertMenuItem(hmDst, uInsert, TRUE, &miiSrc))
  146. {
  147. goto MM_Exit;
  148. }
  149. }
  150. // Ensure the correct number of separators at the beginning of the
  151. // inserted menu items
  152. if (uInsert == 0)
  153. {
  154. if (bAlreadySeparated)
  155. {
  156. DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
  157. }
  158. }
  159. else
  160. {
  161. if (_SHIsMenuSeparator(hmDst, uInsert-1))
  162. {
  163. if (bAlreadySeparated)
  164. {
  165. DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
  166. }
  167. }
  168. else
  169. {
  170. if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
  171. {
  172. // Add a separator between the menus
  173. InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  174. }
  175. }
  176. }
  177. MM_Exit:
  178. return(uIDMax);
  179. }
  180. CCabItemMenu::CCabItemMenu(HWND hwndOwner, CCabFolder*pcf, LPCABITEM *apit, UINT cpit)
  181. : m_lSel(8)
  182. {
  183. m_hwndOwner = hwndOwner;
  184. m_pcfHere = pcf;
  185. pcf->AddRef();
  186. // No need to check return value here; check in QueryInterface
  187. m_lSel.AddItems(apit, cpit);
  188. }
  189. CCabItemMenu::~CCabItemMenu()
  190. {
  191. m_pcfHere->Release();
  192. }
  193. // *** IUnknown methods ***
  194. STDMETHODIMP CCabItemMenu::QueryInterface(
  195. REFIID riid,
  196. LPVOID FAR* ppvObj)
  197. {
  198. *ppvObj = NULL;
  199. if (m_lSel.GetState() == CCabItemList::State_OutOfMem)
  200. {
  201. return(E_OUTOFMEMORY);
  202. }
  203. LPUNKNOWN pObj;
  204. if (riid == IID_IUnknown)
  205. {
  206. pObj = (LPUNKNOWN)(IUnknown*)((IContextMenu*)this);
  207. // The (IShellFolder*) ^^^ up there is to disambiguate :) the reference
  208. }
  209. else if (riid == IID_IContextMenu)
  210. {
  211. pObj = (LPUNKNOWN)(IContextMenu*)this;
  212. }
  213. else
  214. {
  215. return(E_NOINTERFACE);
  216. }
  217. pObj->AddRef();
  218. *ppvObj = pObj;
  219. return(NOERROR);
  220. }
  221. STDMETHODIMP_(ULONG) CCabItemMenu::AddRef(void)
  222. {
  223. return(m_cRef.AddRef());
  224. }
  225. STDMETHODIMP_(ULONG) CCabItemMenu::Release(void)
  226. {
  227. if (!m_cRef.Release())
  228. {
  229. delete this;
  230. return(0);
  231. }
  232. return(m_cRef.GetRef());
  233. }
  234. // *** IContextMenu methods ***
  235. STDMETHODIMP CCabItemMenu::QueryContextMenu(
  236. HMENU hmenu,
  237. UINT indexMenu,
  238. UINT idCmdFirst,
  239. UINT idCmdLast,
  240. UINT uFlags)
  241. {
  242. HMENU hmMerge = LoadPopupMenu(MENU_ITEMCONTEXT, 0);
  243. if (!hmMerge)
  244. {
  245. return(E_OUTOFMEMORY);
  246. }
  247. if (CMF_DVFILE & uFlags)
  248. {
  249. // No "copy" item on the file menu:
  250. RemoveMenu(hmMerge, IDC_ITEM_COPY, MF_BYCOMMAND);
  251. }
  252. UINT idMax = Cab_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast,
  253. MM_ADDSEPARATOR);
  254. DestroyMenu(hmMerge);
  255. SetMenuDefaultItem(hmenu, IDC_ITEM_EXTRACT+idCmdFirst, FALSE);
  256. return(ResultFromShort(idMax - idCmdFirst));
  257. }
  258. STDMETHODIMP CCabItemMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
  259. {
  260. if (lpici->cbSize < SIZEOF(CMINVOKECOMMANDINFO))
  261. {
  262. return E_INVALIDARG;
  263. }
  264. if (HIWORD(lpici->lpVerb))
  265. {
  266. // Deal with string commands
  267. LPCMINVOKECOMMANDINFOEX lpicix = (LPCMINVOKECOMMANDINFOEX) lpici; // This value is only usable when fCmdInfoEx is true
  268. #ifdef UNICODE
  269. BOOL fUnicode = FALSE;
  270. if ((lpici->cbSize >= CMICEXSIZE_NT4) && ((lpici->fMask & CMIC_MASK_UNICODE) == CMIC_MASK_UNICODE))
  271. {
  272. fUnicode = TRUE;
  273. }
  274. #endif
  275. LPCTSTR pszVerb;
  276. #ifdef UNICODE
  277. WCHAR szVerb[MAX_PATH];
  278. if (!fUnicode || lpicix->lpVerbW == NULL)
  279. {
  280. SHAnsiToUnicode(lpici->lpVerb, szVerb, ARRAYSIZE(szVerb));
  281. pszVerb = szVerb;
  282. }
  283. else
  284. pszVerb = lpicix->lpVerbW;
  285. #else
  286. pszVerb = lpici->lpVerb;
  287. #endif
  288. UINT idCmd = 0;
  289. if (NULL != pszVerb)
  290. {
  291. if (0 == lstrcmpi(pszVerb, TEXT("copy")))
  292. {
  293. idCmd = IDC_ITEM_COPY;
  294. }
  295. else if (0 == lstrcmpi(pszVerb, TEXT("extract")))
  296. {
  297. idCmd = IDC_ITEM_EXTRACT;
  298. }
  299. }
  300. lpici->lpVerb = (LPCSTR) IntToPtr(idCmd);
  301. }
  302. switch ((UINT)LOWORD((DWORD_PTR)lpici->lpVerb))
  303. {
  304. case IDC_ITEM_EXTRACT:
  305. {
  306. TCHAR szHere[MAX_PATH];
  307. if (!m_pcfHere->GetPath(szHere))
  308. {
  309. return(E_UNEXPECTED);
  310. }
  311. UINT cPidls = m_lSel.GetCount();
  312. if (0 == cPidls)
  313. {
  314. return(E_UNEXPECTED);
  315. }
  316. IDataObject* pdo = (IDataObject*) (new CCabObj(m_hwndOwner, m_pcfHere,
  317. m_lSel.GetArray(), cPidls));
  318. if (NULL == pdo)
  319. {
  320. return(E_OUTOFMEMORY);
  321. }
  322. // the object is created with a zero ref count, so we need to temporarily
  323. // bump it up if we're going to use it:
  324. pdo->AddRef();
  325. CCabExtract ceHere(szHere);
  326. BOOL fResult = ceHere.ExtractToFolder(m_hwndOwner, pdo, ShouldExtract, (LPARAM)this);
  327. pdo->Release();
  328. return fResult ? S_OK : E_FAIL;
  329. }
  330. case IDC_ITEM_COPY:
  331. {
  332. UINT cPidls = m_lSel.GetCount();
  333. if (cPidls > 0)
  334. {
  335. IDataObject* pObj = (IDataObject*) (new CCabObj(m_hwndOwner, m_pcfHere,
  336. m_lSel.GetArray(), cPidls));
  337. if (NULL != pObj)
  338. {
  339. // the object is created with a zero ref count, so we need to temporarily
  340. // bump it up if we're going to use it:
  341. pObj->AddRef();
  342. HRESULT hr = OleSetClipboard(pObj);
  343. pObj->Release();
  344. return hr;
  345. }
  346. }
  347. return E_FAIL;
  348. }
  349. default:
  350. return(E_INVALIDARG);
  351. }
  352. return(NOERROR);
  353. }
  354. STDMETHODIMP CCabItemMenu::GetCommandString(
  355. UINT_PTR idCmd,
  356. UINT uType,
  357. UINT * pwReserved,
  358. LPSTR pszName,
  359. UINT cchMax)
  360. {
  361. return(E_NOTIMPL);
  362. }
  363. HGLOBAL * CALLBACK CCabItemMenu::ShouldExtract(LPCTSTR pszFile, DWORD dwSize, UINT date,
  364. UINT time, UINT attribs, LPARAM lParam)
  365. {
  366. CCabItemMenu *pThis = (CCabItemMenu*)lParam;
  367. if (pThis->m_lSel.IsInList(pszFile, dwSize, date, time, attribs))
  368. {
  369. return(EXTRACT_TRUE);
  370. }
  371. // Copy nothing for now
  372. return(EXTRACT_FALSE);
  373. }
  374. HMENU CCabItemMenu::LoadPopupMenu(UINT id, UINT uSubMenu)
  375. {
  376. HMENU hmParent = LoadMenu(g_ThisDll.GetInstance(), MAKEINTRESOURCE(id));
  377. if (!hmParent)
  378. {
  379. return(NULL);
  380. }
  381. HMENU hmPopup = GetSubMenu(hmParent, 0);
  382. RemoveMenu(hmParent, uSubMenu, MF_BYPOSITION);
  383. DestroyMenu(hmParent);
  384. return(hmPopup);
  385. }