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