2 * COPYRIGHT: See COPYING in the top level directory
4 * FILE: boot/freeldr/freeldr/ui/tuimenu.c
5 * PURPOSE: UI Menu Functions
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Brian Palmer (brianp@sginet.com)
10 /* INCLUDES ******************************************************************/
14 /* FUNCTIONS *****************************************************************/
17 TuiDisplayMenu(PCSTR MenuHeader
,
19 BOOLEAN ShowBootOptions
,
22 ULONG DefaultMenuItem
,
24 ULONG
* SelectedMenuItem
,
26 UiMenuKeyPressFilterCallback KeyPressFilter
)
28 UI_MENU_INFO MenuInformation
;
29 ULONG LastClockSecond
;
30 ULONG CurrentClockSecond
;
34 // Before taking any default action if there is no timeout,
35 // check whether the supplied key filter callback function
36 // may handle a specific user keypress. If it does, the
37 // timeout is cancelled.
39 if (!MenuTimeOut
&& KeyPressFilter
&& MachConsKbHit())
44 KeyPress
= MachConsGetCh();
47 // Is it extended? Then get the extended key
49 if (!KeyPress
) KeyPress
= MachConsGetCh();
52 // Call the supplied key filter callback function to see
53 // if it is going to handle this keypress.
55 if (KeyPressFilter(KeyPress
))
58 // It processed the key character, cancel the timeout
65 // Check if there's no timeout
70 // Return the default selected item
72 if (SelectedMenuItem
) *SelectedMenuItem
= DefaultMenuItem
;
77 // Setup the MENU_INFO structure
79 MenuInformation
.MenuHeader
= MenuHeader
;
80 MenuInformation
.MenuFooter
= MenuFooter
;
81 MenuInformation
.ShowBootOptions
= ShowBootOptions
;
82 MenuInformation
.MenuItemList
= MenuItemList
;
83 MenuInformation
.MenuItemCount
= MenuItemCount
;
84 MenuInformation
.MenuTimeRemaining
= MenuTimeOut
;
85 MenuInformation
.SelectedMenuItem
= DefaultMenuItem
;
88 // Calculate the size of the menu box
90 TuiCalcMenuBoxSize(&MenuInformation
);
95 UiVtbl
.DrawMenu(&MenuInformation
);
98 // Get the current second of time
100 LastClockSecond
= ArcGetTime()->Second
;
108 // Process key presses
110 KeyPress
= TuiProcessMenuKeyboardEvent(&MenuInformation
,
114 // Check for ENTER or ESC
116 if (KeyPress
== KEY_ENTER
) break;
117 if (CanEscape
&& KeyPress
== KEY_ESC
) return FALSE
;
120 // Update the date & time
123 VideoCopyOffScreenBufferToVRAM();
126 // Check if there is a countdown
128 if (MenuInformation
.MenuTimeRemaining
> 0)
131 // Get the updated time, seconds only
133 CurrentClockSecond
= ArcGetTime()->Second
;
136 // Check if more then a second has now elapsed
138 if (CurrentClockSecond
!= LastClockSecond
)
141 // Update the time information
143 LastClockSecond
= CurrentClockSecond
;
144 MenuInformation
.MenuTimeRemaining
--;
149 TuiDrawMenuBox(&MenuInformation
);
150 VideoCopyOffScreenBufferToVRAM();
153 else if (MenuInformation
.MenuTimeRemaining
== 0)
156 // A time out occurred, exit this loop and return default OS
165 // Return the selected item
167 if (SelectedMenuItem
) *SelectedMenuItem
= MenuInformation
.SelectedMenuItem
;
173 TuiCalcMenuBoxSize(PUI_MENU_INFO MenuInfo
)
181 // Height is the menu item count plus 2 (top border & bottom border)
183 Height
= MenuInfo
->MenuItemCount
+ 2;
184 Height
-= 1; // Height is zero-based
189 for(i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
192 // Get the string length and make it become the new width if necessary
194 if (MenuInfo
->MenuItemList
[i
])
196 Length
= (ULONG
)strlen(MenuInfo
->MenuItemList
[i
]);
197 if (Length
> Width
) Width
= Length
;
202 // Allow room for left & right borders, plus 8 spaces on each side
207 // Check if we're drawing a centered menu
212 // Calculate the menu box area for a centered menu
214 MenuInfo
->Left
= (UiScreenWidth
- Width
) / 2;
215 MenuInfo
->Top
= (((UiScreenHeight
- TUI_TITLE_BOX_CHAR_HEIGHT
) -
216 Height
) / 2) + TUI_TITLE_BOX_CHAR_HEIGHT
;
221 // Put the menu in the default left-corner position
228 // The other margins are the same
230 MenuInfo
->Right
= (MenuInfo
->Left
) + Width
;
231 MenuInfo
->Bottom
= (MenuInfo
->Top
) + Height
;
235 TuiDrawMenu(PUI_MENU_INFO MenuInfo
)
245 // Update the status bar
247 UiVtbl
.DrawStatusText("Use \x18 and \x19 to select, then press ENTER.");
252 TuiDrawMenuBox(MenuInfo
);
255 // Draw each line of the menu
257 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
259 TuiDrawMenuItem(MenuInfo
, i
);
262 /* Display the boot options if needed */
263 if (MenuInfo
->ShowBootOptions
)
265 DisplayBootTimeOptions();
268 VideoCopyOffScreenBufferToVRAM();
273 TuiDrawMenuBox(PUI_MENU_INFO MenuInfo
)
275 CHAR MenuLineText
[80], TempString
[80];
279 // Draw the menu box if requested
283 UiDrawBox(MenuInfo
->Left
,
291 ATTR(UiMenuFgColor
, UiMenuBgColor
));
295 // If there is a timeout draw the time remaining
297 if (MenuInfo
->MenuTimeRemaining
>= 0)
300 // Copy the integral time text string, and remove the last 2 chars
302 strcpy(TempString
, UiTimeText
);
303 i
= (ULONG
)strlen(TempString
);
304 TempString
[i
- 2] = 0;
307 // Display the first part of the string and the remaining time
309 strcpy(MenuLineText
, TempString
);
310 _itoa(MenuInfo
->MenuTimeRemaining
, TempString
, 10);
311 strcat(MenuLineText
, TempString
);
314 // Add the last 2 chars
316 strcat(MenuLineText
, &UiTimeText
[i
- 2]);
319 // Check if this is a centered menu
324 // Display it in the center of the menu
326 UiDrawText(MenuInfo
->Right
- (ULONG
)strlen(MenuLineText
) - 1,
329 ATTR(UiMenuFgColor
, UiMenuBgColor
));
334 // Display under the menu directly
337 MenuInfo
->Bottom
+ 4,
339 ATTR(UiMenuFgColor
, UiMenuBgColor
));
345 // Erase the timeout string with spaces, and 0-terminate for sure
347 for (i
=0; i
<sizeof(MenuLineText
)-1; i
++)
349 MenuLineText
[i
] = ' ';
351 MenuLineText
[sizeof(MenuLineText
)-1] = 0;
354 // Draw this "empty" string to erase
358 UiDrawText(MenuInfo
->Right
- (ULONG
)strlen(MenuLineText
) - 1,
361 ATTR(UiMenuFgColor
, UiMenuBgColor
));
366 MenuInfo
->Bottom
+ 4,
368 ATTR(UiMenuFgColor
, UiMenuBgColor
));
375 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
378 // Check if it's a separator
380 if (MenuInfo
->MenuItemList
[i
] == NULL
)
383 // Draw the separator line
385 UiDrawText(MenuInfo
->Left
,
386 MenuInfo
->Top
+ i
+ 1,
388 ATTR(UiMenuFgColor
, UiMenuBgColor
));
389 UiDrawText(MenuInfo
->Right
,
390 MenuInfo
->Top
+ i
+ 1,
392 ATTR(UiMenuFgColor
, UiMenuBgColor
));
399 TuiDrawMenuItem(PUI_MENU_INFO MenuInfo
,
400 ULONG MenuItemNumber
)
403 CHAR MenuLineText
[80];
406 ULONG SpaceRight
= 0;
407 UCHAR Attribute
= ATTR(UiTextColor
, UiMenuBgColor
);
410 // Check if using centered menu
415 // We will want the string centered so calculate
416 // how many spaces will be to the left and right
418 SpaceTotal
= (MenuInfo
->Right
- MenuInfo
->Left
- 2) -
419 (ULONG
)(MenuInfo
->MenuItemList
[MenuItemNumber
] ?
420 strlen(MenuInfo
->MenuItemList
[MenuItemNumber
]) : 0);
421 SpaceLeft
= (SpaceTotal
/ 2) + 1;
422 SpaceRight
= (SpaceTotal
- SpaceLeft
) + 1;
425 // Insert the spaces on the left
427 for (i
= 0; i
< SpaceLeft
; i
++) MenuLineText
[i
] = ' ';
428 MenuLineText
[i
] = '\0';
433 // Simply left-align it
435 MenuLineText
[0] = '\0';
436 strcat(MenuLineText
, " ");
440 // Now append the text string
442 if (MenuInfo
->MenuItemList
[MenuItemNumber
])
443 strcat(MenuLineText
, MenuInfo
->MenuItemList
[MenuItemNumber
]);
446 // Check if using centered menu, and add spaces on the right if so
448 if (UiCenterMenu
) for (i
=0; i
< SpaceRight
; i
++) strcat(MenuLineText
, " ");
451 // If it is a separator
453 if (MenuInfo
->MenuItemList
[MenuItemNumber
] == NULL
)
456 // Make it a separator line and use menu colors
458 memset(MenuLineText
, 0, 80);
459 memset(MenuLineText
, 0xC4, (MenuInfo
->Right
- MenuInfo
->Left
- 1));
460 Attribute
= ATTR(UiMenuFgColor
, UiMenuBgColor
);
462 else if (MenuItemNumber
== MenuInfo
->SelectedMenuItem
)
465 // If this is the selected item, use the selected colors
467 Attribute
= ATTR(UiSelectedTextColor
, UiSelectedTextBgColor
);
473 UiDrawText(MenuInfo
->Left
+ 1,
474 MenuInfo
->Top
+ 1 + MenuItemNumber
,
481 TuiProcessMenuKeyboardEvent(PUI_MENU_INFO MenuInfo
,
482 UiMenuKeyPressFilterCallback KeyPressFilter
)
485 ULONG Selected
, Count
;
488 // Check for a keypress
493 // Check if the timeout is not already complete
495 if (MenuInfo
->MenuTimeRemaining
!= -1)
498 // Cancel it and remove it
500 MenuInfo
->MenuTimeRemaining
= -1;
501 TuiDrawMenuBox(MenuInfo
); // FIXME: Remove for minimal UI too
507 KeyEvent
= MachConsGetCh();
510 // Is it extended? Then get the extended key
512 if (!KeyEvent
) KeyEvent
= MachConsGetCh();
515 // Call the supplied key filter callback function to see
516 // if it is going to handle this keypress.
518 if ((KeyPressFilter
) && (KeyPressFilter(KeyEvent
)))
521 // It processed the key character, so redraw and exit
523 UiVtbl
.DrawMenu(MenuInfo
);
530 if ((KeyEvent
== KEY_UP
) || (KeyEvent
== KEY_DOWN
) ||
531 (KeyEvent
== KEY_HOME
) || (KeyEvent
== KEY_END
))
534 // Get the current selected item and count
536 Selected
= MenuInfo
->SelectedMenuItem
;
537 Count
= MenuInfo
->MenuItemCount
- 1;
540 // Check the key and change the selected menu item
542 if ((KeyEvent
== KEY_UP
) && (Selected
> 0))
545 // Deselect previous item and go up
547 MenuInfo
->SelectedMenuItem
--;
548 TuiDrawMenuItem(MenuInfo
, Selected
);
551 // Skip past any separators
552 if ((Selected
> 0) &&
553 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
555 MenuInfo
->SelectedMenuItem
--;
558 else if ( ((KeyEvent
== KEY_UP
) && (Selected
== 0)) ||
559 (KeyEvent
== KEY_END
) )
564 MenuInfo
->SelectedMenuItem
= Count
;
565 TuiDrawMenuItem(MenuInfo
, Selected
);
567 else if ((KeyEvent
== KEY_DOWN
) && (Selected
< Count
))
570 // Deselect previous item and go down
572 MenuInfo
->SelectedMenuItem
++;
573 TuiDrawMenuItem(MenuInfo
, Selected
);
576 // Skip past any separators
577 if ((Selected
< Count
) &&
578 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
580 MenuInfo
->SelectedMenuItem
++;
583 else if ( ((KeyEvent
== KEY_DOWN
) && (Selected
== Count
)) ||
584 (KeyEvent
== KEY_HOME
) )
587 // Go to the beginning
589 MenuInfo
->SelectedMenuItem
= 0;
590 TuiDrawMenuItem(MenuInfo
, Selected
);
594 // Select new item and update video buffer
596 TuiDrawMenuItem(MenuInfo
, MenuInfo
->SelectedMenuItem
);
597 VideoCopyOffScreenBufferToVRAM();
602 // Return the pressed key