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.

661 lines
16 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. spmenu.c
  5. Abstract:
  6. Text setup menu support.
  7. Author:
  8. Ted Miller (tedm) 8-September-1993
  9. Revision History:
  10. --*/
  11. #include "spprecmp.h"
  12. #pragma hdrstop
  13. #define MENUITEM_NORMAL 0x00000000
  14. #define MENUITEM_STATIC 0x00000001
  15. typedef struct _MENU_ITEM {
  16. PWSTR Text;
  17. ULONG Flags;
  18. ULONG LeftX;
  19. ULONG_PTR UserData;
  20. ULONG OriginalLength;
  21. } MENU_ITEM, *PMENU_ITEM;
  22. typedef struct _MENU {
  23. PMENU_ITEM Items;
  24. ULONG ItemCount;
  25. ULONG TopY;
  26. ULONG Height;
  27. ULONG LeftX;
  28. ULONG Width;
  29. ULONG TopDisplayedIndex;
  30. BOOLEAN MoreUp,MoreDown;
  31. } MENU, *PMENU;
  32. VOID
  33. pSpMnDrawMenu(
  34. IN PMENU pMenu,
  35. IN ULONG SelectedIndex,
  36. IN BOOLEAN DrawFrame,
  37. IN BOOLEAN IndicateMore,
  38. IN PWSTR MoreUpText,
  39. IN PWSTR MoreDownText
  40. );
  41. PVOID
  42. SpMnCreate(
  43. IN ULONG LeftX,
  44. IN ULONG TopY,
  45. IN ULONG Width,
  46. IN ULONG Height
  47. )
  48. /*++
  49. Routine Description:
  50. Create a new menu by allocating space for a new menu structure
  51. and initializing its fields.
  52. Arguments:
  53. LeftX - supplies the 0-based X coordinate of the leftmost column
  54. of the menu.
  55. TopY - supplies the 0-based Y coordinate of the topmost line
  56. of the menu.
  57. Width - supplies the maximum displayed width for lines in the menu.
  58. Height - supplies the maximum displayed height of the menu.
  59. The menu will scroll if it is too long to fit in the
  60. allotted space.
  61. Return Value:
  62. Menu handle (expressed as a pvoid) of NULL if memory couldn't
  63. be allocated.
  64. --*/
  65. {
  66. PMENU p;
  67. if(p = SpMemAlloc(sizeof(MENU))) {
  68. RtlZeroMemory(p,sizeof(MENU));
  69. if(p->Items = SpMemAlloc(0)) {
  70. p->LeftX = LeftX;
  71. p->TopY = TopY;
  72. p->Width = Width;
  73. p->Height = Height;
  74. } else {
  75. SpMemFree(p);
  76. p = NULL;
  77. }
  78. }
  79. return(p);
  80. }
  81. VOID
  82. SpMnDestroy(
  83. IN PVOID Menu
  84. )
  85. /*++
  86. Routine Description:
  87. Destroy a menu, releasing all memory associated with it.
  88. Arguments:
  89. Menu - supplies handle to menu to destroy.
  90. Return Value:
  91. None.
  92. --*/
  93. {
  94. PMENU pMenu = Menu;
  95. ULONG u;
  96. for(u=0; u<pMenu->ItemCount; u++) {
  97. if(pMenu->Items[u].Text) {
  98. SpMemFree(pMenu->Items[u].Text);
  99. }
  100. }
  101. SpMemFree(pMenu->Items);
  102. SpMemFree(pMenu);
  103. }
  104. BOOLEAN
  105. SpMnAddItem(
  106. IN PVOID Menu,
  107. IN PWSTR Text,
  108. IN ULONG LeftX,
  109. IN ULONG Width,
  110. IN BOOLEAN Selectable,
  111. IN ULONG_PTR UserData
  112. )
  113. /*++
  114. Routine Description:
  115. Add an item to a menu.
  116. Arguments:
  117. Menu - supplies handle to menu to which the item is to be added.
  118. Text - supplies text that comprises the menu selection. This routine
  119. will make a copy of the text.
  120. LeftX - supplies 0-based x coordinate of leftmost character of the text
  121. when it is displayed.
  122. Width - supplies width in characters of the field for this selection.
  123. If this is larger than the number of characters in the text, then
  124. the text is padded to the right with blanks when highlighted.
  125. Selectable - if FALSE, then this text is static -- ie, not selectable.
  126. UserData - supplies a ulong's worth of caller-specific data to be associated
  127. with this menu item.
  128. Return Value:
  129. TRUE if the menu item was added successfully; FALSE if insufficient memory.
  130. --*/
  131. {
  132. PMENU pMenu = Menu;
  133. PMENU_ITEM p;
  134. ULONG TextLen;
  135. ULONG PaddedLen;
  136. PWSTR String;
  137. ULONG u;
  138. ULONG ColumnLen;
  139. ULONG FillLen;
  140. //
  141. // Build a string that is padded to the right with blanks to make
  142. // it the right width.
  143. //
  144. TextLen = wcslen(Text);
  145. PaddedLen = max(TextLen,Width);
  146. ColumnLen = SplangGetColumnCount(Text);
  147. FillLen = (PaddedLen <= ColumnLen) ? 0 : PaddedLen - ColumnLen;
  148. String = SpMemAlloc((PaddedLen+1)*sizeof(WCHAR));
  149. if(!String) {
  150. return(FALSE);
  151. }
  152. wcsncpy(String,Text,TextLen);
  153. for(u=0; u<FillLen; u++) {
  154. String[TextLen+u] = L' ';
  155. }
  156. String[TextLen+u] = 0;
  157. //
  158. // Make space for the item.
  159. //
  160. if((p = SpMemRealloc(pMenu->Items,(pMenu->ItemCount+1) * sizeof(MENU_ITEM))) == NULL) {
  161. SpMemFree(String);
  162. return(FALSE);
  163. }
  164. pMenu->Items = p;
  165. //
  166. // Calculate the address of the new menu item and
  167. // indicate that there is now an additional item in the menu.
  168. //
  169. p = &pMenu->Items[pMenu->ItemCount++];
  170. //
  171. // Set the fields of the menu.
  172. //
  173. p->LeftX = LeftX;
  174. p->UserData = UserData;
  175. p->Flags = Selectable ? MENUITEM_NORMAL : MENUITEM_STATIC;
  176. p->Text = String;
  177. p->OriginalLength = TextLen;
  178. return(TRUE);
  179. }
  180. PWSTR
  181. SpMnGetText(
  182. IN PVOID Menu,
  183. IN ULONG_PTR UserData
  184. )
  185. {
  186. PMENU pMenu = Menu;
  187. ULONG i;
  188. for(i=0; i<pMenu->ItemCount; i++) {
  189. if(pMenu->Items[i].UserData == UserData) {
  190. return(pMenu->Items[i].Text);
  191. }
  192. }
  193. return(NULL);
  194. }
  195. PWSTR
  196. SpMnGetTextDup(
  197. IN PVOID Menu,
  198. IN ULONG_PTR UserData
  199. )
  200. {
  201. PMENU pMenu = Menu;
  202. ULONG i;
  203. PWSTR p;
  204. for(i=0; i<pMenu->ItemCount; i++) {
  205. if(pMenu->Items[i].UserData == UserData) {
  206. //
  207. // Make a duplicate; leave off trailing pad spaces.
  208. //
  209. p = SpMemAlloc((pMenu->Items[i].OriginalLength+1)*sizeof(WCHAR));
  210. wcsncpy(p,pMenu->Items[i].Text,pMenu->Items[i].OriginalLength);
  211. p[pMenu->Items[i].OriginalLength] = 0;
  212. return(p);
  213. }
  214. }
  215. return(NULL);
  216. }
  217. VOID
  218. SpMnDisplay(
  219. IN PVOID Menu,
  220. IN ULONG_PTR UserDataOfHighlightedItem,
  221. IN BOOLEAN Framed,
  222. IN PULONG ValidKeys,
  223. IN PULONG Mnemonics, OPTIONAL
  224. IN PMENU_CALLBACK_ROUTINE NewHighlightCallback, OPTIONAL
  225. OUT PULONG KeyPressed,
  226. OUT PULONG_PTR UserDataOfSelectedItem
  227. )
  228. /*++
  229. Routine Description:
  230. Display a menu and accept keystrokes.
  231. When the user presses a menu keystroke (up/down arrow keys), this
  232. routine automatically updates the highlight and calls a callback function
  233. to inform the caller that a new item has the highlight.
  234. When the user presses a keystroke in a list provided by the caller,
  235. this routine returns, providing information about the key pressed and
  236. the item that was highlighted when the key was pressed.
  237. Arguments:
  238. Menu - supplies handle to menu to be displayed.
  239. UserDataOfHighlightedItem - supplies user data of the menu item which
  240. is to receive the highlight initially.
  241. Framed - if TRUE, then draw a single-line bordera around the menu.
  242. ValidKeys - supplies a list of keystrokes that cause this routine to
  243. return to the caller. The list must be terminated with a 0 entry.
  244. NewHighlightCallback - If specified, supplies a routine to be called
  245. when a new item has received the highlight.
  246. KeyPressed - receives the key press that caused this routine to exit.
  247. This will be a valid from the ValidKeys array.
  248. UserDataOfSelectedItem - receives the UserData of the item that had the
  249. highlight when the user pressed a key in ValidKeys.
  250. Return Value:
  251. None.
  252. --*/
  253. {
  254. ULONG ValidMenuKeys[3] = { KEY_UP, KEY_DOWN, 0 };
  255. ULONG key;
  256. PMENU pMenu = Menu;
  257. ULONG SelectedIndex,OldIndex;
  258. BOOLEAN FoundNewItem;
  259. ULONG NewTopDisplayedIndex;
  260. BOOLEAN MustScroll;
  261. PWSTR MoreUpText,MoreDownText;
  262. //
  263. // Get the text for the text that indicate that there are more
  264. // selections.
  265. //
  266. SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_MORE_UP);
  267. MoreUpText = SpDupStringW(TemporaryBuffer);
  268. SpFormatMessage(TemporaryBuffer,sizeof(TemporaryBuffer),SP_TEXT_MORE_DOWN);
  269. MoreDownText = SpDupStringW(TemporaryBuffer);
  270. //
  271. // Locate the seleccted item.
  272. //
  273. for(SelectedIndex=0; SelectedIndex<pMenu->ItemCount; SelectedIndex++) {
  274. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)
  275. && (pMenu->Items[SelectedIndex].UserData == UserDataOfHighlightedItem))
  276. {
  277. break;
  278. }
  279. }
  280. ASSERT(SelectedIndex < pMenu->ItemCount);
  281. //
  282. // Make sure the selected item will be visible when we draw the menu.
  283. //
  284. pMenu->TopDisplayedIndex = 0;
  285. while(SelectedIndex >= pMenu->TopDisplayedIndex + pMenu->Height) {
  286. pMenu->TopDisplayedIndex += pMenu->Height;
  287. }
  288. //
  289. // Draw the menu itself.
  290. //
  291. pSpMnDrawMenu(pMenu,SelectedIndex,Framed,Framed,MoreUpText,MoreDownText);
  292. while(1) {
  293. //
  294. // Wait for a valid keypress.
  295. //
  296. key = SpWaitValidKey(ValidKeys,ValidMenuKeys,Mnemonics);
  297. //
  298. // If the key is a menu keystroke, handle it here.
  299. //
  300. FoundNewItem = FALSE;
  301. MustScroll = FALSE;
  302. OldIndex = SelectedIndex;
  303. switch(key) {
  304. case KEY_UP:
  305. //
  306. // Locate the previous selectable item.
  307. //
  308. if(SelectedIndex) {
  309. for(SelectedIndex=SelectedIndex-1; (LONG)SelectedIndex>=0; SelectedIndex--) {
  310. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)) {
  311. FoundNewItem = TRUE;
  312. break;
  313. }
  314. }
  315. if(FoundNewItem) {
  316. //
  317. // Figure out whether we have to scroll the menu.
  318. //
  319. if(SelectedIndex < pMenu->TopDisplayedIndex) {
  320. MustScroll = TRUE;
  321. NewTopDisplayedIndex = SelectedIndex;
  322. }
  323. } else {
  324. //
  325. // If the first lines are static text, there might be no
  326. // way to get them back on the screen -- the tests above
  327. // fail in this case. So if the user hits the up arrow
  328. // when he's at the topmost selectable item but there are
  329. // static items above him, we'll simply scroll the menu
  330. // so that item #0 is at the top.
  331. //
  332. FoundNewItem = TRUE;
  333. NewTopDisplayedIndex = 0;
  334. MustScroll = TRUE;
  335. SelectedIndex = OldIndex;
  336. }
  337. }
  338. break;
  339. case KEY_DOWN:
  340. //
  341. // Locate the next selectable item.
  342. //
  343. if(SelectedIndex < pMenu->ItemCount) {
  344. for(SelectedIndex=SelectedIndex+1; SelectedIndex < pMenu->ItemCount; SelectedIndex++) {
  345. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)) {
  346. FoundNewItem = TRUE;
  347. break;
  348. }
  349. }
  350. if(FoundNewItem) {
  351. //
  352. // Figure out whether we have to scroll the menu.
  353. //
  354. if(SelectedIndex >= pMenu->TopDisplayedIndex + pMenu->Height) {
  355. MustScroll = TRUE;
  356. NewTopDisplayedIndex = pMenu->TopDisplayedIndex + SelectedIndex - OldIndex;
  357. }
  358. }
  359. }
  360. break;
  361. default:
  362. //
  363. // User pressed a non-menu key.
  364. //
  365. *KeyPressed = key;
  366. *UserDataOfSelectedItem = pMenu->Items[SelectedIndex].UserData;
  367. SpMemFree(MoreUpText);
  368. SpMemFree(MoreDownText);
  369. return;
  370. }
  371. if(FoundNewItem) {
  372. //
  373. // Unhighlight the currently selected item.
  374. //
  375. SpvidDisplayString(
  376. pMenu->Items[OldIndex].Text,
  377. DEFAULT_ATTRIBUTE,
  378. pMenu->Items[OldIndex].LeftX,
  379. pMenu->TopY + OldIndex - pMenu->TopDisplayedIndex
  380. );
  381. //
  382. // Highlight the newly selected item. This may involve
  383. // scrolling the menu.
  384. //
  385. if(MustScroll) {
  386. //
  387. // Redraw the menu so the newly highlighted line is in view.
  388. //
  389. pMenu->TopDisplayedIndex = NewTopDisplayedIndex;
  390. pSpMnDrawMenu(pMenu,SelectedIndex,FALSE,Framed,MoreUpText,MoreDownText);
  391. }
  392. //
  393. // Highlight the newly selected item.
  394. //
  395. SpvidDisplayString(
  396. pMenu->Items[SelectedIndex].Text,
  397. ATT_BG_WHITE | ATT_FG_BLUE,
  398. pMenu->Items[SelectedIndex].LeftX,
  399. pMenu->TopY + SelectedIndex - pMenu->TopDisplayedIndex
  400. );
  401. //
  402. // Inform the caller.
  403. //
  404. if(NewHighlightCallback) {
  405. NewHighlightCallback(pMenu->Items[SelectedIndex].UserData);
  406. }
  407. } else {
  408. SelectedIndex = OldIndex;
  409. }
  410. }
  411. }
  412. VOID
  413. pSpMnDrawMenu(
  414. IN PMENU pMenu,
  415. IN ULONG SelectedIndex,
  416. IN BOOLEAN DrawFrame,
  417. IN BOOLEAN IndicateMore,
  418. IN PWSTR MoreUpText,
  419. IN PWSTR MoreDownText
  420. )
  421. {
  422. ULONG item;
  423. BOOLEAN MoreUp,MoreDown,MoreStatusChanged;
  424. //
  425. // Blank out the on-screen menu display.
  426. //
  427. SpvidClearScreenRegion(
  428. pMenu->LeftX,
  429. pMenu->TopY,
  430. pMenu->Width,
  431. pMenu->Height,
  432. DEFAULT_BACKGROUND
  433. );
  434. MoreUp = (BOOLEAN)(pMenu->TopDisplayedIndex > 0);
  435. MoreDown = (BOOLEAN)(pMenu->TopDisplayedIndex + pMenu->Height < pMenu->ItemCount);
  436. //
  437. // We want to force the frame to be drawn if there is a change in whether
  438. // there are more selections above or below us.
  439. //
  440. MoreStatusChanged = (BOOLEAN)( IndicateMore
  441. && ( (pMenu->MoreUp != MoreUp)
  442. || (pMenu->MoreDown != MoreDown)
  443. )
  444. );
  445. if(DrawFrame || MoreStatusChanged) {
  446. ASSERT(pMenu->LeftX);
  447. ASSERT(pMenu->TopY);
  448. SpDrawFrame(
  449. pMenu->LeftX-1,
  450. pMenu->Width+2,
  451. pMenu->TopY-1,
  452. pMenu->Height+2,
  453. DEFAULT_ATTRIBUTE,
  454. FALSE
  455. );
  456. }
  457. //
  458. // Draw each item that is currently on-screen.
  459. //
  460. ASSERT(pMenu->TopDisplayedIndex < pMenu->ItemCount);
  461. for(item = pMenu->TopDisplayedIndex;
  462. item < min(pMenu->TopDisplayedIndex+pMenu->Height,pMenu->ItemCount);
  463. item++)
  464. {
  465. SpvidDisplayString(
  466. pMenu->Items[item].Text,
  467. (UCHAR)((item == SelectedIndex) ? ATT_BG_WHITE | ATT_FG_BLUE : DEFAULT_ATTRIBUTE),
  468. pMenu->Items[item].LeftX,
  469. pMenu->TopY + item - pMenu->TopDisplayedIndex
  470. );
  471. }
  472. //
  473. // If there are more selections above or below us,
  474. // indicate so by placing a small bit of text on the frame.
  475. // Note that the arrow chars can sometimes be DBCS.
  476. //
  477. if(MoreStatusChanged) {
  478. if(MoreUp) {
  479. SpvidDisplayString(
  480. MoreUpText,
  481. DEFAULT_ATTRIBUTE,
  482. pMenu->LeftX + pMenu->Width - SplangGetColumnCount(MoreUpText) - 1,
  483. pMenu->TopY - 1
  484. );
  485. }
  486. if(MoreDown) {
  487. SpvidDisplayString(
  488. MoreDownText,
  489. DEFAULT_ATTRIBUTE,
  490. pMenu->LeftX + pMenu->Width - SplangGetColumnCount(MoreDownText) - 1,
  491. pMenu->TopY + pMenu->Height
  492. );
  493. }
  494. pMenu->MoreUp = MoreUp;
  495. pMenu->MoreDown = MoreDown;
  496. }
  497. }