Leaked source code of windows server 2003
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.

405 lines
11 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: mnkey.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Menu Keyboard Handling Routines
  7. *
  8. * History:
  9. * 10-10-90 JimA Cleanup.
  10. * 03-18-91 IanJa Window revalidation added
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. int xxxClientFindMnemChar(
  15. PUNICODE_STRING pstrSrc,
  16. WCHAR ch,
  17. BOOL fFirst,
  18. BOOL fPrefix);
  19. /* MenuSwitch commands */
  20. #define CMDSWITCH 1
  21. #define CMDQUERY 2
  22. /***************************************************************************\
  23. * FindNextValidMenuItem
  24. *
  25. * !
  26. *
  27. * History:
  28. \***************************************************************************/
  29. UINT MNFindNextValidItem(
  30. PMENU pMenu,
  31. int i,
  32. int dir,
  33. UINT flags)
  34. {
  35. int iStart;
  36. BOOL cont = TRUE;
  37. int cItems = pMenu->cItems;
  38. PITEM pItem;
  39. if ((i < 0) && (dir > 0))
  40. // going forward from beginning -- stop after last menu item
  41. i = iStart = cItems;
  42. else if ((i >= cItems) && (dir < 0))
  43. // going backward from end -- stop after first menu item
  44. i = iStart = -1;
  45. else
  46. iStart = i;
  47. if (!cItems)
  48. return(MFMWFP_NOITEM);
  49. // b#8997 - if we have these conditions and enter
  50. // loop will go blistic ( infin )
  51. // fix: jump over code and come loop to i == iStart will now stop us
  52. if ( ( i == 0 ) && ( cItems == 1 ) && ( dir > 0 ) )
  53. {
  54. dir = 0;
  55. goto artsquickndirtybugfix;
  56. }
  57. //
  58. // Loop thru menu items til (1) we find a valid item
  59. // or (2) we make it back to the start item (iStart)
  60. while (TRUE) {
  61. i += dir;
  62. if ((i == iStart) || (dir == 0))
  63. // we made it back to start item -- return NOT FOUND
  64. return MFMWFP_NOITEM;
  65. // keep 'i' in the range: 0 <= i < cItems
  66. if (i >= cItems) {
  67. i = -1;
  68. continue;
  69. }
  70. else if (i < 0) {
  71. i = cItems;
  72. continue;
  73. }
  74. artsquickndirtybugfix:
  75. pItem = pMenu->rgItems + i;
  76. // skip ownerdraw - seperators even though there not NULL
  77. if (TestMFT(pItem, MFT_SEPARATOR)) {
  78. //
  79. // Skip non-separator (if asked) empty items. With hot-tracking,
  80. // it is acceptable for them to be selected. In truth, it was possible
  81. // in Win3.1 too, but less likely.
  82. //
  83. if (!(flags & MNF_DONTSKIPSEPARATORS)) {
  84. continue;
  85. }
  86. } else if ((pItem->hbmp >= HBMMENU_MBARFIRST) && (pItem->hbmp <= HBMMENU_MBARLAST)) {
  87. /*
  88. * Skip close & minimize & restore buttons
  89. */
  90. continue;
  91. }
  92. // return index of found item
  93. return(i);
  94. }
  95. //
  96. // We should never get here!
  97. //
  98. UserAssert(FALSE);
  99. }
  100. /***************************************************************************\
  101. * MKF_FindMenuItemInColumn
  102. *
  103. * Finds closest item in the pull-down menu's next "column".
  104. *
  105. * History:
  106. \***************************************************************************/
  107. UINT MNFindItemInColumn(
  108. PMENU pMenu,
  109. UINT idxB,
  110. int dir,
  111. BOOL fRoot)
  112. {
  113. int dxMin;
  114. int dyMin;
  115. int dxMax;
  116. int dyMax;
  117. int xB;
  118. int yB;
  119. UINT idxE;
  120. UINT idxR;
  121. UINT cItems;
  122. PITEM pItem;
  123. cItems = pMenu->cItems;
  124. idxR = MFMWFP_NOITEM;
  125. idxE = MNFindNextValidItem(pMenu, MFMWFP_NOITEM, dir, 0);
  126. if (idxE == -1)
  127. goto End;
  128. dxMin = dyMin = 20000;
  129. if (idxB >= pMenu->cItems)
  130. return idxR;
  131. pItem = &pMenu->rgItems[idxB];
  132. xB = pItem->xItem;
  133. yB = pItem->yItem;
  134. while (cItems-- > 0 &&
  135. (idxB = MNFindNextValidItem(pMenu, idxB, dir, 0)) != idxE &&
  136. (idxB != MFMWFP_NOITEM)) {
  137. pItem = &pMenu->rgItems[idxB];
  138. dxMax = xB - pItem->xItem;
  139. dyMax = yB - pItem->yItem;
  140. if (dxMax < 0)
  141. dxMax = (-dxMax);
  142. if (dyMax < 0)
  143. dyMax = (-dyMax);
  144. // See if this item is nearer than the last item found
  145. // --------------------------------------------------------
  146. // (fRoot || dxMax) -- this condition means that if it's
  147. // not the actual menu bar menu that we're dealing with,
  148. // then the item below/above (same X value as) the selected
  149. // item is not a valid one to move to
  150. if ((dyMax < dyMin) && (fRoot || dxMax) && dxMax <= dxMin) {
  151. dxMin = dxMax;
  152. dyMin = dyMax;
  153. idxR = idxB;
  154. }
  155. }
  156. End:
  157. return idxR;
  158. }
  159. /***************************************************************************\
  160. * MKF_FindMenuChar
  161. *
  162. * Translates Virtual cursor key movements into pseudo-ascii values. Maps a
  163. * character to an item number.
  164. *
  165. * History:
  166. \***************************************************************************/
  167. UINT xxxMNFindChar(
  168. PMENU pMenu,
  169. UINT ch,
  170. int idxC,
  171. LPINT lpr) /* Put match type here */
  172. {
  173. int idxFirst = MFMWFP_NOITEM;
  174. int idxB;
  175. int idxF;
  176. int rT;
  177. LPWSTR lpstr;
  178. PITEM pItem;
  179. if (ch == 0)
  180. return 0;
  181. /*
  182. * First time thru go for the very first menu.
  183. */
  184. idxF = MFMWFP_NOITEM;
  185. rT = 0;
  186. idxB = idxC;
  187. if (idxB < 0)
  188. // if (idxB & 0x8000)
  189. idxB = MNFindNextValidItem(pMenu, pMenu->cItems, MFMWFP_NOITEM, MNF_DONTSKIPSEPARATORS);
  190. do {
  191. INT idxPrev;
  192. idxPrev = idxC;
  193. idxC = MNFindNextValidItem(pMenu, idxC, 1, MNF_DONTSKIPSEPARATORS);
  194. if (idxC == MFMWFP_NOITEM || idxC == idxFirst)
  195. break;
  196. if (idxFirst == MFMWFP_NOITEM)
  197. idxFirst = idxC;
  198. pItem = &pMenu->rgItems[idxC];
  199. if (pItem->lpstr != NULL) {
  200. if (pItem->cch != 0) {
  201. UNICODE_STRING strMnem;
  202. lpstr = TextPointer(pItem->lpstr);
  203. if (*lpstr == CH_HELPPREFIX) {
  204. /*
  205. * Skip help prefix if it is there so that we can mnemonic
  206. * to the first character of a right justified string.
  207. */
  208. lpstr++;
  209. }
  210. RtlInitUnicodeString(&strMnem, lpstr);
  211. if (((rT = (UINT)xxxClientFindMnemChar(&strMnem,
  212. (WCHAR)ch, TRUE, TRUE)) == 0x0080) &&
  213. (idxF == MFMWFP_NOITEM))
  214. idxF = idxC;
  215. }
  216. }
  217. if (idxC == idxPrev) {
  218. break; // no progress - break inf. loop
  219. }
  220. } while (rT != 1 && idxB != idxC);
  221. *lpr = rT;
  222. if (rT == 1)
  223. return idxC;
  224. return idxF;
  225. }
  226. /***************************************************************************\
  227. * xxxMenuKeyFilter
  228. *
  229. * !
  230. *
  231. * Revalidation notes:
  232. * o Routine assumes it is called with pMenuState->hwndMenu non-NULL and valid.
  233. * o If one or more of the popup menu windows is unexpectedly destroyed, this is
  234. * detected in xxxMenuWndProc(), which sets pMenuState->fSabotaged and calls
  235. * xxxKillMenuState(). Therefore, if we return from an xxxRoutine with
  236. * pMenuState->fSabotaged set, we must abort immediately.
  237. * o If pMenuState->hwndMenu is unexpectedly destroyed, we abort only if we
  238. * need to use the corresponding pwndMenu.
  239. * o pMenuState->hwndMenu may be supplied as a parameter to various routines
  240. * (eg: xxxNextItem), whether valid or not.
  241. * o Any label preceded with xxx (eg: xxxMKF_UnlockAndExit) may be reached with
  242. * pMenuState->hwndMenu invalid.
  243. * o If this routine is not called while in xxxMenuLoop(), then it must
  244. * clear pMenuState->fSabotaged before returning.
  245. *
  246. * History:
  247. \***************************************************************************/
  248. void xxxMNKeyFilter(
  249. PPOPUPMENU ppopupMenu,
  250. PMENUSTATE pMenuState,
  251. UINT ch)
  252. {
  253. BOOL fLocalInsideMenuLoop = pMenuState->fInsideMenuLoop;
  254. if (pMenuState->fButtonDown) {
  255. /*
  256. * Ignore keystrokes while the mouse is pressed (except ESC).
  257. */
  258. return;
  259. }
  260. if (!pMenuState->fInsideMenuLoop) {
  261. /*
  262. * Need to send the WM_INITMENU message before we pull down the menu.
  263. */
  264. if (!xxxMNStartMenu(ppopupMenu, KEYBDHOLD)) {
  265. return;
  266. }
  267. pMenuState->fInsideMenuLoop = TRUE;
  268. }
  269. switch (ch) {
  270. case 0:
  271. /*
  272. * If we get a WM_KEYDOWN alt key and then a KEYUP alt key, we need to
  273. * activate the first item on the menu. ie. user hits and releases alt
  274. * key so just select first item. USER sends us a SC_KEYMENU with
  275. * lParam 0 when the user does this.
  276. */
  277. xxxMNSelectItem(ppopupMenu, pMenuState, 0);
  278. break;
  279. case MENUCHILDSYSMENU:
  280. if (!TestwndChild(ppopupMenu->spwndNotify)) {
  281. /*
  282. * Change made to fix MDI problem: child window gets a keymenu,
  283. * and pops up sysmenu of frame when maximized. Need to act like
  284. * MENUCHAR if hwndMenu is a top-level.
  285. */
  286. goto MenuCharHandler;
  287. }
  288. /*
  289. * else fall through.
  290. */
  291. case MENUSYSMENU:
  292. if (!TestWF(ppopupMenu->spwndNotify, WFSYSMENU)) {
  293. xxxMessageBeep(0);
  294. goto MenuCancel;
  295. }
  296. /*
  297. * Popup any hierarchies we have.
  298. */
  299. xxxMNCloseHierarchy(ppopupMenu, pMenuState);
  300. if (!ppopupMenu->fIsSysMenu && ppopupMenu->spmenuAlternate)
  301. xxxMNSwitchToAlternateMenu(ppopupMenu);
  302. if (!ppopupMenu->fIsSysMenu) {
  303. /*
  304. * If no system menu, get out.
  305. */
  306. goto MenuCancel;
  307. }
  308. MNPositionSysMenu(ppopupMenu->spwndPopupMenu, ppopupMenu->spmenu);
  309. xxxMNSelectItem(ppopupMenu, pMenuState, 0);
  310. xxxMNOpenHierarchy(ppopupMenu, pMenuState);
  311. ppopupMenu->fToggle = FALSE;
  312. break;
  313. default:
  314. /*
  315. * Handle ALT-Character sequences for items on top level menu bar.
  316. * Note that fInsideMenuLoop may be set to false on return from this
  317. * function if the app decides to return 1 to the WM_MENUCHAR message.
  318. * We detect this and not enter MenuLoop if fInsideMenuLoop is reset
  319. * to false.
  320. */
  321. MenuCharHandler:
  322. xxxMNChar(ppopupMenu, pMenuState, ch);
  323. if (ppopupMenu->posSelectedItem == MFMWFP_NOITEM) {
  324. /*
  325. * No selection found.
  326. */
  327. goto MenuCancel;
  328. }
  329. break;
  330. }
  331. if (!fLocalInsideMenuLoop && pMenuState->fInsideMenuLoop) {
  332. xxxMNLoop(ppopupMenu, pMenuState, 0, FALSE);
  333. }
  334. return;
  335. MenuCancel:
  336. pMenuState->fModelessMenu = FALSE;
  337. if (!ppopupMenu->fInCancel) {
  338. xxxMNDismiss(pMenuState);
  339. }
  340. UserAssert(!pMenuState->fInsideMenuLoop && !pMenuState->fMenuStarted);
  341. return;
  342. }