[FREELDR]
[reactos.git] / reactos / boot / freeldr / freeldr / ui / tuimenu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: FreeLoader
4 * FILE: freeldr/ui/tuimenu.c
5 * PURPOSE: UI Menu Functions
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Brian Palmer (brianp@sginet.com)
8 */
9
10 /* INCLUDES ******************************************************************/
11 #ifndef _M_ARM
12 #include <freeldr.h>
13
14 /* FUNCTIONS *****************************************************************/
15
16 BOOLEAN
17 TuiDisplayMenu(PCSTR MenuHeader,
18 PCSTR MenuFooter,
19 PCSTR MenuItemList[],
20 ULONG MenuItemCount,
21 ULONG DefaultMenuItem,
22 LONG MenuTimeOut,
23 ULONG* SelectedMenuItem,
24 BOOLEAN CanEscape,
25 UiMenuKeyPressFilterCallback KeyPressFilter)
26 {
27 UI_MENU_INFO MenuInformation;
28 ULONG LastClockSecond;
29 ULONG CurrentClockSecond;
30 ULONG KeyPress;
31
32 //
33 // Check if there's no timeout
34 if (!MenuTimeOut)
35 {
36 //
37 // Return the default selected item
38 //
39 if (SelectedMenuItem) *SelectedMenuItem = DefaultMenuItem;
40 return TRUE;
41 }
42
43 //
44 // Setup the MENU_INFO structure
45 //
46 MenuInformation.MenuHeader = MenuHeader;
47 MenuInformation.MenuFooter = MenuFooter;
48 MenuInformation.MenuItemList = MenuItemList;
49 MenuInformation.MenuItemCount = MenuItemCount;
50 MenuInformation.MenuTimeRemaining = MenuTimeOut;
51 MenuInformation.SelectedMenuItem = DefaultMenuItem;
52
53 //
54 // Calculate the size of the menu box
55 //
56 TuiCalcMenuBoxSize(&MenuInformation);
57
58 //
59 // Draw the menu
60 //
61 UiVtbl.DrawMenu(&MenuInformation);
62
63 //
64 // Get the current second of time
65 //
66 LastClockSecond = ArcGetTime()->Second;
67
68 //
69 // Process keys
70 //
71 while (TRUE)
72 {
73 //
74 // Process key presses
75 //
76 KeyPress = TuiProcessMenuKeyboardEvent(&MenuInformation,
77 KeyPressFilter);
78
79 //
80 // Check for ENTER or ESC
81 //
82 if (KeyPress == KEY_ENTER) break;
83 if (CanEscape && KeyPress == KEY_ESC) return FALSE;
84
85 //
86 // Update the date & time
87 //
88 TuiUpdateDateTime();
89 VideoCopyOffScreenBufferToVRAM();
90
91 //
92 // Check if there is a countdown
93 //
94 if (MenuInformation.MenuTimeRemaining)
95 {
96 //
97 // Get the updated time, seconds only
98 //
99 CurrentClockSecond = ArcGetTime()->Second;
100
101 //
102 // Check if more then a second has now elapsed
103 //
104 if (CurrentClockSecond != LastClockSecond)
105 {
106 //
107 // Update the time information
108 //
109 LastClockSecond = CurrentClockSecond;
110 MenuInformation.MenuTimeRemaining--;
111
112 //
113 // Update the menu
114 //
115 TuiDrawMenuBox(&MenuInformation);
116 VideoCopyOffScreenBufferToVRAM();
117 }
118 }
119 else
120 {
121 //
122 // A time out occurred, exit this loop and return default OS
123 //
124 break;
125 }
126
127 MachHwIdle();
128 }
129
130 //
131 // Return the selected item
132 //
133 if (SelectedMenuItem) *SelectedMenuItem = MenuInformation.SelectedMenuItem;
134 return TRUE;
135 }
136
137 VOID
138 NTAPI
139 TuiCalcMenuBoxSize(PUI_MENU_INFO MenuInfo)
140 {
141 ULONG i;
142 ULONG Width = 0;
143 ULONG Height;
144 ULONG Length;
145
146 //
147 // Height is the menu item count plus 2 (top border & bottom border)
148 //
149 Height = MenuInfo->MenuItemCount + 2;
150 Height -= 1; // Height is zero-based
151
152 //
153 // Loop every item
154 //
155 for(i = 0; i < MenuInfo->MenuItemCount; i++)
156 {
157 //
158 // Get the string length and make it become the new width if necessary
159 //
160 Length = (ULONG)strlen(MenuInfo->MenuItemList[i]);
161 if (Length > Width) Width = Length;
162 }
163
164 //
165 // Allow room for left & right borders, plus 8 spaces on each side
166 //
167 Width += 18;
168
169 //
170 // Check if we're drawing a centered menu
171 //
172 if (UiCenterMenu)
173 {
174 //
175 // Calculate the menu box area for a centered menu
176 //
177 MenuInfo->Left = (UiScreenWidth - Width) / 2;
178 MenuInfo->Top = (((UiScreenHeight - TUI_TITLE_BOX_CHAR_HEIGHT) -
179 Height) / 2) + TUI_TITLE_BOX_CHAR_HEIGHT;
180 }
181 else
182 {
183 //
184 // Put the menu in the default left-corner position
185 //
186 MenuInfo->Left = -1;
187 MenuInfo->Top = 4;
188 }
189
190 //
191 // The other margins are the same
192 //
193 MenuInfo->Right = (MenuInfo->Left) + Width;
194 MenuInfo->Bottom = (MenuInfo->Top) + Height;
195 }
196
197 VOID
198 TuiDrawMenu(PUI_MENU_INFO MenuInfo)
199 {
200 ULONG i;
201
202 //
203 // Draw the backdrop
204 //
205 UiDrawBackdrop();
206
207 //
208 // Update the status bar
209 //
210 UiVtbl.DrawStatusText("Use \x18 and \x19 to select, then press ENTER.");
211
212 //
213 // Draw the menu box
214 //
215 TuiDrawMenuBox(MenuInfo);
216
217 //
218 // Draw each line of the menu
219 //
220 for (i = 0; i < MenuInfo->MenuItemCount; i++)
221 {
222 TuiDrawMenuItem(MenuInfo, i);
223 }
224
225 VideoCopyOffScreenBufferToVRAM();
226 }
227
228 VOID
229 NTAPI
230 TuiDrawMenuBox(PUI_MENU_INFO MenuInfo)
231 {
232 CHAR MenuLineText[80], TempString[80];
233 ULONG i;
234
235 //
236 // Draw the menu box if requested
237 //
238 if (UiMenuBox)
239 {
240 UiDrawBox(MenuInfo->Left,
241 MenuInfo->Top,
242 MenuInfo->Right,
243 MenuInfo->Bottom,
244 D_VERT,
245 D_HORZ,
246 FALSE, // Filled
247 TRUE, // Shadow
248 ATTR(UiMenuFgColor, UiMenuBgColor));
249 }
250
251 //
252 // If there is a timeout draw the time remaining
253 //
254 if (MenuInfo->MenuTimeRemaining >= 0)
255 {
256 //
257 // Copy the integral time text string, and remove the last 2 chars
258 //
259 strcpy(TempString, UiTimeText);
260 i = (ULONG)strlen(TempString);
261 TempString[i - 2] = 0;
262
263 //
264 // Display the first part of the string and the remaining time
265 //
266 strcpy(MenuLineText, TempString);
267 _itoa(MenuInfo->MenuTimeRemaining, TempString, 10);
268 strcat(MenuLineText, TempString);
269
270 //
271 // Add the last 2 chars
272 //
273 strcat(MenuLineText, &UiTimeText[i - 2]);
274
275 //
276 // Check if this is a centered menu
277 //
278 if (UiCenterMenu)
279 {
280 //
281 // Display it in the center of the menu
282 //
283 UiDrawText(MenuInfo->Right - (ULONG)strlen(MenuLineText) - 1,
284 MenuInfo->Bottom,
285 MenuLineText,
286 ATTR(UiMenuFgColor, UiMenuBgColor));
287 }
288 else
289 {
290 //
291 // Display under the menu directly
292 //
293 UiDrawText(0,
294 MenuInfo->Bottom + 4,
295 MenuLineText,
296 ATTR(UiMenuFgColor, UiMenuBgColor));
297 }
298 }
299 else
300 {
301 //
302 // Erase the timeout string with spaces, and 0-terminate for sure
303 //
304 for (i=0; i<sizeof(MenuLineText)-1; i++)
305 {
306 MenuLineText[i] = ' ';
307 }
308 MenuLineText[sizeof(MenuLineText)-1] = 0;
309
310 //
311 // Draw this "empty" string to erase
312 //
313 if (UiCenterMenu)
314 {
315 UiDrawText(MenuInfo->Right - (ULONG)strlen(MenuLineText) - 1,
316 MenuInfo->Bottom,
317 MenuLineText,
318 ATTR(UiMenuFgColor, UiMenuBgColor));
319 }
320 else
321 {
322 UiDrawText(0,
323 MenuInfo->Bottom + 4,
324 MenuLineText,
325 ATTR(UiMenuFgColor, UiMenuBgColor));
326 }
327 }
328
329 //
330 // Loop each item
331 //
332 for (i = 0; i < MenuInfo->MenuItemCount; i++)
333 {
334 //
335 // Check if it's a separator
336 //
337 if (!(_stricmp(MenuInfo->MenuItemList[i], "SEPARATOR")))
338 {
339 //
340 // Draw the separator line
341 //
342 UiDrawText(MenuInfo->Left,
343 MenuInfo->Top + i + 1,
344 "\xC7",
345 ATTR(UiMenuFgColor, UiMenuBgColor));
346 UiDrawText(MenuInfo->Right,
347 MenuInfo->Top + i + 1,
348 "\xB6",
349 ATTR(UiMenuFgColor, UiMenuBgColor));
350 }
351 }
352 }
353
354 VOID
355 NTAPI
356 TuiDrawMenuItem(PUI_MENU_INFO MenuInfo,
357 ULONG MenuItemNumber)
358 {
359 ULONG i;
360 CHAR MenuLineText[80];
361 ULONG SpaceTotal;
362 ULONG SpaceLeft;
363 ULONG SpaceRight = 0;
364 UCHAR Attribute = ATTR(UiTextColor, UiMenuBgColor);
365
366 //
367 // Check if using centered menu
368 //
369 if (UiCenterMenu)
370 {
371 //
372 // We will want the string centered so calculate
373 // how many spaces will be to the left and right
374 //
375 SpaceTotal = (MenuInfo->Right - MenuInfo->Left - 2) -
376 (ULONG)strlen(MenuInfo->MenuItemList[MenuItemNumber]);
377 SpaceLeft = (SpaceTotal / 2) + 1;
378 SpaceRight = (SpaceTotal - SpaceLeft) + 1;
379
380 //
381 // Insert the spaces on the left
382 //
383 for (i = 0; i < SpaceLeft; i++) MenuLineText[i] = ' ';
384 MenuLineText[i] = '\0';
385 }
386 else
387 {
388 //
389 // Simply left-align it
390 //
391 MenuLineText[0] = '\0';
392 strcat(MenuLineText, " ");
393 }
394
395 //
396 // Now append the text string
397 //
398 strcat(MenuLineText, MenuInfo->MenuItemList[MenuItemNumber]);
399
400 //
401 // Check if using centered menu, and add spaces on the right if so
402 //
403 if (UiCenterMenu) for (i=0; i < SpaceRight; i++) strcat(MenuLineText, " ");
404
405 //
406 // If it is a separator
407 //
408 if (!(_stricmp(MenuInfo->MenuItemList[MenuItemNumber], "SEPARATOR")))
409 {
410 //
411 // Make it a separator line and use menu colors
412 //
413 memset(MenuLineText, 0, 80);
414 memset(MenuLineText, 0xC4, (MenuInfo->Right - MenuInfo->Left - 1));
415 Attribute = ATTR(UiMenuFgColor, UiMenuBgColor);
416 }
417 else if (MenuItemNumber == MenuInfo->SelectedMenuItem)
418 {
419 //
420 // If this is the selected item, use the selected colors
421 //
422 Attribute = ATTR(UiSelectedTextColor, UiSelectedTextBgColor);
423 }
424
425 //
426 // Draw the item
427 //
428 UiDrawText(MenuInfo->Left + 1,
429 MenuInfo->Top + 1 + MenuItemNumber,
430 MenuLineText,
431 Attribute);
432 }
433
434 ULONG
435 NTAPI
436 TuiProcessMenuKeyboardEvent(PUI_MENU_INFO MenuInfo,
437 UiMenuKeyPressFilterCallback KeyPressFilter)
438 {
439 ULONG KeyEvent = 0;
440 ULONG Selected, Count;
441
442 //
443 // Check for a keypress
444 //
445 if (MachConsKbHit())
446 {
447 //
448 // Check if the timeout is not already complete
449 //
450 if (MenuInfo->MenuTimeRemaining != -1)
451 {
452 //
453 // Cancel it and remove it
454 //
455 MenuInfo->MenuTimeRemaining = -1;
456 TuiDrawMenuBox(MenuInfo); // FIXME: Remove for minimal UI too
457 }
458
459 //
460 // Get the key
461 //
462 KeyEvent = MachConsGetCh();
463
464 //
465 // Is it extended? Then get the extended key
466 //
467 if (!KeyEvent) KeyEvent = MachConsGetCh();
468
469 //
470 // Call the supplied key filter callback function to see
471 // if it is going to handle this keypress.
472 //
473 if ((KeyPressFilter) && (KeyPressFilter(KeyEvent)))
474 {
475 //
476 // It processed the key character, so redraw and exit
477 //
478 UiVtbl.DrawMenu(MenuInfo);
479 return 0;
480 }
481
482 //
483 // Process the key
484 //
485 if ((KeyEvent == KEY_UP) || (KeyEvent == KEY_DOWN))
486 {
487 //
488 // Get the current selected item and count
489 //
490 Selected = MenuInfo->SelectedMenuItem;
491 Count = MenuInfo->MenuItemCount - 1;
492
493 //
494 // Check if this was a key up and there's a selected menu item
495 //
496 if ((KeyEvent == KEY_UP) && (Selected))
497 {
498 //
499 // Update the menu (Deselect previous item)
500 //
501 MenuInfo->SelectedMenuItem--;
502 TuiDrawMenuItem(MenuInfo, Selected);
503 Selected--;
504
505 // Skip past any separators
506 if ((Selected) &&
507 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
508 {
509 MenuInfo->SelectedMenuItem--;
510 }
511 }
512 else if ((KeyEvent == KEY_DOWN) && (Selected < Count))
513 {
514 //
515 // Update the menu (deselect previous item)
516 //
517 MenuInfo->SelectedMenuItem++;
518 TuiDrawMenuItem(MenuInfo, Selected);
519 Selected++;
520
521 // Skip past any separators
522 if ((Selected < Count) &&
523 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
524 {
525 MenuInfo->SelectedMenuItem++;
526 }
527 }
528
529 //
530 // Select new item and update video buffer
531 //
532 TuiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem);
533 VideoCopyOffScreenBufferToVRAM();
534 }
535 }
536
537 //
538 // Return the pressed key
539 //
540 return KeyEvent;
541 }
542 #endif