summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Universal/SetupBrowserDxe/Ui.c')
-rw-r--r--MdeModulePkg/Universal/SetupBrowserDxe/Ui.c4027
1 files changed, 4027 insertions, 0 deletions
diff --git a/MdeModulePkg/Universal/SetupBrowserDxe/Ui.c b/MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
new file mode 100644
index 0000000000..7c246b60e1
--- /dev/null
+++ b/MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
@@ -0,0 +1,4027 @@
+/** @file
+Utility functions for User Interface functions.
+
+Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Setup.h"
+
+LIST_ENTRY gMenuOption;
+LIST_ENTRY gMenuList;
+MENU_REFRESH_ENTRY *gMenuRefreshHead; // Menu list used for refresh timer opcode.
+MENU_REFRESH_ENTRY *gMenuEventGuidRefreshHead; // Menu list used for refresh event guid opcode.
+
+//
+// Search table for UiDisplayMenu()
+//
+SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
+ {
+ SCAN_UP,
+ UiUp,
+ },
+ {
+ SCAN_DOWN,
+ UiDown,
+ },
+ {
+ SCAN_PAGE_UP,
+ UiPageUp,
+ },
+ {
+ SCAN_PAGE_DOWN,
+ UiPageDown,
+ },
+ {
+ SCAN_ESC,
+ UiReset,
+ },
+ {
+ SCAN_LEFT,
+ UiLeft,
+ },
+ {
+ SCAN_RIGHT,
+ UiRight,
+ }
+};
+
+UINTN mScanCodeNumber = sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]);
+
+SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
+ {
+ UiNoOperation,
+ CfUiNoOperation,
+ },
+ {
+ UiSelect,
+ CfUiSelect,
+ },
+ {
+ UiUp,
+ CfUiUp,
+ },
+ {
+ UiDown,
+ CfUiDown,
+ },
+ {
+ UiLeft,
+ CfUiLeft,
+ },
+ {
+ UiRight,
+ CfUiRight,
+ },
+ {
+ UiReset,
+ CfUiReset,
+ },
+ {
+ UiPageUp,
+ CfUiPageUp,
+ },
+ {
+ UiPageDown,
+ CfUiPageDown
+ },
+ {
+ UiHotKey,
+ CfUiHotKey
+ }
+};
+
+BOOLEAN mInputError;
+BOOLEAN GetLineByWidthFinished = FALSE;
+
+
+/**
+ Set Buffer to Value for Size bytes.
+
+ @param Buffer Memory to set.
+ @param Size Number of bytes to set
+ @param Value Value of the set operation.
+
+**/
+VOID
+SetUnicodeMem (
+ IN VOID *Buffer,
+ IN UINTN Size,
+ IN CHAR16 Value
+ )
+{
+ CHAR16 *Ptr;
+
+ Ptr = Buffer;
+ while ((Size--) != 0) {
+ *(Ptr++) = Value;
+ }
+}
+
+
+/**
+ Initialize Menu option list.
+
+**/
+VOID
+UiInitMenu (
+ VOID
+ )
+{
+ InitializeListHead (&gMenuOption);
+}
+
+
+/**
+ Free Menu option linked list.
+
+**/
+VOID
+UiFreeMenu (
+ VOID
+ )
+{
+ UI_MENU_OPTION *MenuOption;
+
+ while (!IsListEmpty (&gMenuOption)) {
+ MenuOption = MENU_OPTION_FROM_LINK (gMenuOption.ForwardLink);
+ RemoveEntryList (&MenuOption->Link);
+
+ //
+ // We allocated space for this description when we did a GetToken, free it here
+ //
+ if (MenuOption->Skip != 0) {
+ //
+ // For date/time, MenuOption->Description is shared by three Menu Options
+ // Data format : [01/02/2004] [11:22:33]
+ // Line number : 0 0 1 0 0 1
+ //
+ FreePool (MenuOption->Description);
+ }
+ FreePool (MenuOption);
+ }
+}
+
+
+/**
+ Create a menu with specified formset GUID and form ID, and add it as a child
+ of the given parent menu.
+
+ @param Parent The parent of menu to be added.
+ @param HiiHandle Hii handle related to this formset.
+ @param FormSetGuid The Formset Guid of menu to be added.
+ @param FormId The Form ID of menu to be added.
+
+ @return A pointer to the newly added menu or NULL if memory is insufficient.
+
+**/
+UI_MENU_LIST *
+UiAddMenuList (
+ IN OUT UI_MENU_LIST *Parent,
+ IN EFI_HII_HANDLE HiiHandle,
+ IN EFI_GUID *FormSetGuid,
+ IN UINT16 FormId
+ )
+{
+ UI_MENU_LIST *MenuList;
+
+ MenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
+ if (MenuList == NULL) {
+ return NULL;
+ }
+
+ MenuList->Signature = UI_MENU_LIST_SIGNATURE;
+ InitializeListHead (&MenuList->ChildListHead);
+
+ MenuList->HiiHandle = HiiHandle;
+ CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID));
+ MenuList->FormId = FormId;
+ MenuList->Parent = Parent;
+
+ if (Parent == NULL) {
+ //
+ // If parent is not specified, it is the root Form of a Formset
+ //
+ InsertTailList (&gMenuList, &MenuList->Link);
+ } else {
+ InsertTailList (&Parent->ChildListHead, &MenuList->Link);
+ }
+
+ return MenuList;
+}
+
+
+/**
+ Search Menu with given FormId, FormSetGuid and Handle in all cached menu list.
+
+ @param Parent The parent of menu to search.
+ @param Handle Hii handle related to this formset.
+ @param FormSetGuid The Formset GUID of the menu to search.
+ @param FormId The Form ID of menu to search.
+
+ @return A pointer to menu found or NULL if not found.
+
+**/
+UI_MENU_LIST *
+UiFindChildMenuList (
+ IN UI_MENU_LIST *Parent,
+ IN EFI_HII_HANDLE Handle,
+ IN EFI_GUID *FormSetGuid,
+ IN UINT16 FormId
+ )
+{
+ LIST_ENTRY *Link;
+ UI_MENU_LIST *Child;
+ UI_MENU_LIST *MenuList;
+
+ ASSERT (Parent != NULL);
+
+ if (Parent->FormId == FormId && CompareGuid (FormSetGuid, &Parent->FormSetGuid) && Parent->HiiHandle == Handle) {
+ return Parent;
+ }
+
+ Link = GetFirstNode (&Parent->ChildListHead);
+ while (!IsNull (&Parent->ChildListHead, Link)) {
+ Child = UI_MENU_LIST_FROM_LINK (Link);
+
+ MenuList = UiFindChildMenuList (Child, Handle, FormSetGuid, FormId);
+ if (MenuList != NULL) {
+ return MenuList;
+ }
+
+ Link = GetNextNode (&Parent->ChildListHead, Link);
+ }
+
+ return NULL;
+}
+
+
+/**
+ Search Menu with given Handle, FormSetGuid and FormId in all cached menu list.
+
+ @param Handle Hii handle related to this formset.
+ @param FormSetGuid The Formset GUID of the menu to search.
+ @param FormId The Form ID of menu to search.
+
+ @return A pointer to menu found or NULL if not found.
+
+**/
+UI_MENU_LIST *
+UiFindMenuList (
+ IN EFI_HII_HANDLE Handle,
+ IN EFI_GUID *FormSetGuid,
+ IN UINT16 FormId
+ )
+{
+ LIST_ENTRY *Link;
+ UI_MENU_LIST *MenuList;
+ UI_MENU_LIST *Child;
+
+ Link = GetFirstNode (&gMenuList);
+ while (!IsNull (&gMenuList, Link)) {
+ MenuList = UI_MENU_LIST_FROM_LINK (Link);
+
+ Child = UiFindChildMenuList(MenuList, Handle, FormSetGuid, FormId);
+ if (Child != NULL) {
+
+ //
+ // If this form already in the menu history list,
+ // just free the list between old this form.
+ //
+ UiFreeMenuList(&Child->ChildListHead);
+ return Child;
+ }
+
+ Link = GetNextNode (&gMenuList, Link);
+ }
+
+ return NULL;
+}
+
+/**
+ Free Menu list linked list.
+
+ @param MenuListHead One Menu list point in the menu list.
+
+**/
+VOID
+UiFreeMenuList (
+ LIST_ENTRY *MenuListHead
+ )
+{
+ UI_MENU_LIST *MenuList;
+
+ while (!IsListEmpty (MenuListHead)) {
+ MenuList = UI_MENU_LIST_FROM_LINK (MenuListHead->ForwardLink);
+ RemoveEntryList (&MenuList->Link);
+
+ UiFreeMenuList(&MenuList->ChildListHead);
+ FreePool (MenuList);
+ }
+
+}
+
+/**
+ Free Menu option linked list.
+
+**/
+VOID
+UiFreeRefreshList (
+ VOID
+ )
+{
+ MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
+
+ while (gMenuRefreshHead != NULL) {
+ OldMenuRefreshEntry = gMenuRefreshHead->Next;
+ FreePool (gMenuRefreshHead);
+ gMenuRefreshHead = OldMenuRefreshEntry;
+ }
+
+ while (gMenuEventGuidRefreshHead != NULL) {
+ OldMenuRefreshEntry = gMenuEventGuidRefreshHead->Next;
+ if (gMenuEventGuidRefreshHead != NULL) {
+ gBS->CloseEvent(gMenuEventGuidRefreshHead->Event);
+ }
+ FreePool (gMenuEventGuidRefreshHead);
+ gMenuEventGuidRefreshHead = OldMenuRefreshEntry;
+ }
+}
+
+
+/**
+ Process option string for date/time opcode.
+
+ @param MenuOption Menu option point to date/time.
+ @param OptionString Option string input for process.
+ @param AddOptCol Whether need to update MenuOption->OptCol.
+
+**/
+VOID
+ProcessStringForDateTime (
+ UI_MENU_OPTION *MenuOption,
+ CHAR16 *OptionString,
+ BOOLEAN AddOptCol
+ )
+{
+ UINTN Index;
+ UINTN Count;
+ FORM_BROWSER_STATEMENT *Statement;
+
+ ASSERT (MenuOption != NULL && OptionString != NULL);
+
+ Statement = MenuOption->ThisTag;
+
+ //
+ // If leading spaces on OptionString - remove the spaces
+ //
+ for (Index = 0; OptionString[Index] == L' '; Index++) {
+ //
+ // Base on the blockspace to get the option column info.
+ //
+ if (AddOptCol) {
+ MenuOption->OptCol++;
+ }
+ }
+
+ for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
+ OptionString[Count] = OptionString[Index];
+ Count++;
+ }
+ OptionString[Count] = CHAR_NULL;
+
+ //
+ // Enable to suppress field in the opcode base on the flag.
+ //
+ if (Statement->Operand == EFI_IFR_DATE_OP) {
+ //
+ // OptionString format is: <**: **: ****>
+ // |month|day|year|
+ // 4 3 5
+ //
+ if ((Statement->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
+ //
+ // At this point, only "<**:" in the optionstring.
+ // Clean the day's ** field, after clean, the format is "< :"
+ //
+ SetUnicodeMem (&OptionString[1], 2, L' ');
+ } else if ((Statement->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
+ //
+ // At this point, only "**:" in the optionstring.
+ // Clean the month's "**" field, after clean, the format is " :"
+ //
+ SetUnicodeMem (&OptionString[0], 2, L' ');
+ } else if ((Statement->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
+ //
+ // At this point, only "****>" in the optionstring.
+ // Clean the year's "****" field, after clean, the format is " >"
+ //
+ SetUnicodeMem (&OptionString[0], 4, L' ');
+ }
+ } else if (Statement->Operand == EFI_IFR_TIME_OP) {
+ //
+ // OptionString format is: <**: **: **>
+ // |hour|minute|second|
+ // 4 3 3
+ //
+ if ((Statement->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
+ //
+ // At this point, only "<**:" in the optionstring.
+ // Clean the hour's ** field, after clean, the format is "< :"
+ //
+ SetUnicodeMem (&OptionString[1], 2, L' ');
+ } else if ((Statement->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
+ //
+ // At this point, only "**:" in the optionstring.
+ // Clean the minute's "**" field, after clean, the format is " :"
+ //
+ SetUnicodeMem (&OptionString[0], 2, L' ');
+ } else if ((Statement->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
+ //
+ // At this point, only "**>" in the optionstring.
+ // Clean the second's "**" field, after clean, the format is " >"
+ //
+ SetUnicodeMem (&OptionString[0], 2, L' ');
+ }
+ }
+}
+
+/**
+ Refresh question.
+
+ @param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
+**/
+EFI_STATUS
+RefreshQuestion (
+ IN MENU_REFRESH_ENTRY *MenuRefreshEntry
+ )
+{
+ CHAR16 *OptionString;
+ EFI_STATUS Status;
+ UI_MENU_SELECTION *Selection;
+ FORM_BROWSER_STATEMENT *Question;
+
+ Selection = MenuRefreshEntry->Selection;
+ Question = MenuRefreshEntry->MenuOption->ThisTag;
+
+ Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithHiiDriver);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ OptionString = NULL;
+ ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
+
+ if (OptionString != NULL) {
+ //
+ // If old Text is longer than new string, need to clean the old string before paint the newer.
+ // This option is no need for time/date opcode, because time/data opcode has fixed string length.
+ //
+ if ((MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_DATE_OP) &&
+ (MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_TIME_OP)) {
+ ClearLines (
+ MenuRefreshEntry->CurrentColumn,
+ MenuRefreshEntry->CurrentColumn + gOptionBlockWidth - 1,
+ MenuRefreshEntry->CurrentRow,
+ MenuRefreshEntry->CurrentRow,
+ PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
+ );
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
+ ProcessStringForDateTime(MenuRefreshEntry->MenuOption, OptionString, FALSE);
+ PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
+ FreePool (OptionString);
+ }
+
+ //
+ // Question value may be changed, need invoke its Callback()
+ //
+ Status = ProcessCallBackFunction (Selection, Question, EFI_BROWSER_ACTION_CHANGING, FALSE);
+
+ return Status;
+}
+
+/**
+ Refresh the question which has refresh guid event attribute.
+
+ @param Event The event which has this function related.
+ @param Context The input context info related to this event or the status code return to the caller.
+**/
+VOID
+EFIAPI
+RefreshQuestionNotify(
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MENU_REFRESH_ENTRY *MenuRefreshEntry;
+ UI_MENU_SELECTION *Selection;
+
+ //
+ // Reset FormPackage update flag
+ //
+ mHiiPackageListUpdated = FALSE;
+
+ MenuRefreshEntry = (MENU_REFRESH_ENTRY *)Context;
+ ASSERT (MenuRefreshEntry != NULL);
+ Selection = MenuRefreshEntry->Selection;
+
+ RefreshQuestion (MenuRefreshEntry);
+
+ if (mHiiPackageListUpdated) {
+ //
+ // Package list is updated, force to reparse IFR binary of target Formset
+ //
+ mHiiPackageListUpdated = FALSE;
+ Selection->Action = UI_ACTION_REFRESH_FORMSET;
+ }
+}
+
+
+/**
+ Refresh screen.
+
+**/
+EFI_STATUS
+RefreshForm (
+ VOID
+ )
+{
+ MENU_REFRESH_ENTRY *MenuRefreshEntry;
+ EFI_STATUS Status;
+ UI_MENU_SELECTION *Selection;
+
+ if (gMenuRefreshHead != NULL) {
+ //
+ // call from refresh interval process.
+ //
+ MenuRefreshEntry = gMenuRefreshHead;
+ Selection = MenuRefreshEntry->Selection;
+ //
+ // Reset FormPackage update flag
+ //
+ mHiiPackageListUpdated = FALSE;
+
+ do {
+ Status = RefreshQuestion (MenuRefreshEntry);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ MenuRefreshEntry = MenuRefreshEntry->Next;
+
+ } while (MenuRefreshEntry != NULL);
+
+ if (mHiiPackageListUpdated) {
+ //
+ // Package list is updated, force to reparse IFR binary of target Formset
+ //
+ mHiiPackageListUpdated = FALSE;
+ Selection->Action = UI_ACTION_REFRESH_FORMSET;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_TIMEOUT;
+}
+
+
+/**
+ Wait for a given event to fire, or for an optional timeout to expire.
+
+ @param Event The event to wait for
+ @param Timeout An optional timeout value in 100 ns units.
+ @param RefreshInterval Menu refresh interval (in seconds).
+
+ @retval EFI_SUCCESS Event fired before Timeout expired.
+ @retval EFI_TIME_OUT Timout expired before Event fired.
+
+**/
+EFI_STATUS
+UiWaitForSingleEvent (
+ IN EFI_EVENT Event,
+ IN UINT64 Timeout, OPTIONAL
+ IN UINT8 RefreshInterval OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_EVENT TimerEvent;
+ EFI_EVENT WaitList[2];
+
+ if (Timeout != 0) {
+ //
+ // Create a timer event
+ //
+ Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Set the timer event
+ //
+ gBS->SetTimer (
+ TimerEvent,
+ TimerRelative,
+ Timeout
+ );
+
+ //
+ // Wait for the original event or the timer
+ //
+ WaitList[0] = Event;
+ WaitList[1] = TimerEvent;
+ Status = gBS->WaitForEvent (2, WaitList, &Index);
+ gBS->CloseEvent (TimerEvent);
+
+ //
+ // If the timer expired, change the return to timed out
+ //
+ if (!EFI_ERROR (Status) && Index == 1) {
+ Status = EFI_TIMEOUT;
+ }
+ }
+ } else {
+ //
+ // Update screen every second
+ //
+ if (RefreshInterval == 0) {
+ Timeout = ONE_SECOND;
+ } else {
+ Timeout = RefreshInterval * ONE_SECOND;
+ }
+
+ do {
+ Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
+
+ //
+ // Set the timer event
+ //
+ gBS->SetTimer (
+ TimerEvent,
+ TimerRelative,
+ Timeout
+ );
+
+ //
+ // Wait for the original event or the timer
+ //
+ WaitList[0] = Event;
+ WaitList[1] = TimerEvent;
+ Status = gBS->WaitForEvent (2, WaitList, &Index);
+
+ //
+ // If the timer expired, update anything that needs a refresh and keep waiting
+ //
+ if (!EFI_ERROR (Status) && Index == 1) {
+ Status = EFI_TIMEOUT;
+ if (RefreshInterval != 0) {
+ Status = RefreshForm ();
+ }
+ }
+
+ gBS->CloseEvent (TimerEvent);
+ } while (Status == EFI_TIMEOUT);
+ }
+
+ return Status;
+}
+
+
+/**
+ Add one menu option by specified description and context.
+
+ @param String String description for this option.
+ @param Handle Hii handle for the package list.
+ @param Form The form this statement belong to.
+ @param Statement Statement of this Menu Option.
+ @param NumberOfLines Display lines for this Menu Option.
+ @param MenuItemCount The index for this Option in the Menu.
+
+ @retval Pointer Pointer to the added Menu Option.
+
+**/
+UI_MENU_OPTION *
+UiAddMenuOption (
+ IN CHAR16 *String,
+ IN EFI_HII_HANDLE Handle,
+ IN FORM_BROWSER_FORM *Form,
+ IN FORM_BROWSER_STATEMENT *Statement,
+ IN UINT16 NumberOfLines,
+ IN UINT16 MenuItemCount
+ )
+{
+ UI_MENU_OPTION *MenuOption;
+ UINTN Index;
+ UINTN Count;
+
+ Count = 1;
+ MenuOption = NULL;
+
+ if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
+ //
+ // Add three MenuOptions for Date/Time
+ // Data format : [01/02/2004] [11:22:33]
+ // Line number : 0 0 1 0 0 1
+ //
+ NumberOfLines = 0;
+ Count = 3;
+
+ if (Statement->Storage == NULL) {
+ //
+ // For RTC type of date/time, set default refresh interval to be 1 second
+ //
+ if (Statement->RefreshInterval == 0) {
+ Statement->RefreshInterval = 1;
+ }
+ }
+ }
+
+ for (Index = 0; Index < Count; Index++) {
+ MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
+ ASSERT (MenuOption);
+
+ MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
+ MenuOption->Description = String;
+ MenuOption->Handle = Handle;
+ MenuOption->ThisTag = Statement;
+ MenuOption->EntryNumber = MenuItemCount;
+
+ if (Index == 2) {
+ //
+ // Override LineNumber for the MenuOption in Date/Time sequence
+ //
+ MenuOption->Skip = 1;
+ } else {
+ MenuOption->Skip = NumberOfLines;
+ }
+ MenuOption->Sequence = Index;
+
+ if (EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) == ExpressGrayOut ) {
+ MenuOption->GrayOut = TRUE;
+ } else {
+ MenuOption->GrayOut = FALSE;
+ }
+
+ //
+ // If the form or the question has the lock attribute, deal same as grayout.
+ //
+ if (Form->Locked || Statement->Locked) {
+ MenuOption->GrayOut = TRUE;
+ }
+
+ switch (Statement->Operand) {
+ case EFI_IFR_ORDERED_LIST_OP:
+ case EFI_IFR_ONE_OF_OP:
+ case EFI_IFR_NUMERIC_OP:
+ case EFI_IFR_TIME_OP:
+ case EFI_IFR_DATE_OP:
+ case EFI_IFR_CHECKBOX_OP:
+ case EFI_IFR_PASSWORD_OP:
+ case EFI_IFR_STRING_OP:
+ //
+ // User could change the value of these items
+ //
+ MenuOption->IsQuestion = TRUE;
+ break;
+
+ case EFI_IFR_TEXT_OP:
+ if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
+ //
+ // Initializing GrayOut option as TRUE for Text setup options
+ // so that those options will be Gray in colour and un selectable.
+ //
+ MenuOption->GrayOut = TRUE;
+ }
+ //
+ // break skipped on purpose
+ //
+ default:
+ MenuOption->IsQuestion = FALSE;
+ break;
+ }
+
+ if ((Statement->ValueExpression != NULL) ||
+ ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) {
+ MenuOption->ReadOnly = TRUE;
+ if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) {
+ MenuOption->GrayOut = TRUE;
+ }
+ }
+
+ InsertTailList (&gMenuOption, &MenuOption->Link);
+ }
+
+ return MenuOption;
+}
+
+
+/**
+ Routine used to abstract a generic dialog interface and return the selected key or string
+
+ @param NumberOfLines The number of lines for the dialog box
+ @param HotKey Defines whether a single character is parsed
+ (TRUE) and returned in KeyValue or a string is
+ returned in StringBuffer. Two special characters
+ are considered when entering a string, a SCAN_ESC
+ and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
+ string input and returns
+ @param MaximumStringSize The maximum size in bytes of a typed in string
+ (each character is a CHAR16) and the minimum
+ string returned is two bytes
+ @param StringBuffer The passed in pointer to the buffer which will
+ hold the typed in string if HotKey is FALSE
+ @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
+ @param ... A series of (quantity == NumberOfLines) text
+ strings which will be used to construct the dialog
+ box
+
+ @retval EFI_SUCCESS Displayed dialog and received user interaction
+ @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
+ (StringBuffer == NULL) && (HotKey == FALSE))
+ @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
+
+**/
+EFI_STATUS
+EFIAPI
+CreateDialog (
+ IN UINTN NumberOfLines,
+ IN BOOLEAN HotKey,
+ IN UINTN MaximumStringSize,
+ OUT CHAR16 *StringBuffer,
+ OUT EFI_INPUT_KEY *KeyValue,
+ ...
+ )
+{
+ VA_LIST Marker;
+ UINTN Count;
+ EFI_INPUT_KEY Key;
+ UINTN LargestString;
+ CHAR16 *TempString;
+ CHAR16 *BufferedString;
+ CHAR16 *StackString;
+ CHAR16 KeyPad[2];
+ UINTN Start;
+ UINTN Top;
+ UINTN Index;
+ EFI_STATUS Status;
+ BOOLEAN SelectionComplete;
+ UINTN InputOffset;
+ UINTN CurrentAttribute;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ SelectionComplete = FALSE;
+ InputOffset = 0;
+ TempString = AllocateZeroPool (MaximumStringSize * 2);
+ BufferedString = AllocateZeroPool (MaximumStringSize * 2);
+ CurrentAttribute = gST->ConOut->Mode->Attribute;
+
+ ASSERT (TempString);
+ ASSERT (BufferedString);
+
+ //
+ // Zero the outgoing buffer
+ //
+ ZeroMem (StringBuffer, MaximumStringSize);
+
+ if (HotKey) {
+ if (KeyValue == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ if (StringBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Disable cursor
+ //
+ gST->ConOut->EnableCursor (gST->ConOut, FALSE);
+
+ LargestString = 0;
+
+ VA_START (Marker, KeyValue);
+
+ //
+ // Determine the largest string in the dialog box
+ // Notice we are starting with 1 since String is the first string
+ //
+ for (Count = 0; Count < NumberOfLines; Count++) {
+ StackString = VA_ARG (Marker, CHAR16 *);
+
+ if (StackString[0] == L' ') {
+ InputOffset = Count + 1;
+ }
+
+ if ((GetStringWidth (StackString) / 2) > LargestString) {
+ //
+ // Size of the string visually and subtract the width by one for the null-terminator
+ //
+ LargestString = (GetStringWidth (StackString) / 2);
+ }
+ }
+ VA_END (Marker);
+
+ Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
+ Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
+
+ Count = 0;
+
+ //
+ // Display the Popup
+ //
+ VA_START (Marker, KeyValue);
+ CreateSharedPopUp (LargestString, NumberOfLines, Marker);
+ VA_END (Marker);
+
+ //
+ // Take the first key typed and report it back?
+ //
+ if (HotKey) {
+ Status = WaitForKeyStroke (&Key);
+ ASSERT_EFI_ERROR (Status);
+ CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
+
+ } else {
+ do {
+ Status = WaitForKeyStroke (&Key);
+
+ switch (Key.UnicodeChar) {
+ case CHAR_NULL:
+ switch (Key.ScanCode) {
+ case SCAN_ESC:
+ FreePool (TempString);
+ FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_DEVICE_ERROR;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ SelectionComplete = TRUE;
+ FreePool (TempString);
+ FreePool (BufferedString);
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_SUCCESS;
+ break;
+
+ case CHAR_BACKSPACE:
+ if (StringBuffer[0] != CHAR_NULL) {
+ for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
+ TempString[Index] = StringBuffer[Index];
+ }
+ //
+ // Effectively truncate string by 1 character
+ //
+ TempString[Index - 1] = CHAR_NULL;
+ StrCpy (StringBuffer, TempString);
+ }
+ //
+ // break skipped on purpose
+ //
+
+ default:
+ //
+ // If it is the beginning of the string, don't worry about checking maximum limits
+ //
+ if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
+ StrnCpy (TempString, &Key.UnicodeChar, 1);
+ } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
+ KeyPad[0] = Key.UnicodeChar;
+ KeyPad[1] = CHAR_NULL;
+ StrCat (StringBuffer, KeyPad);
+ StrCat (TempString, KeyPad);
+ }
+ //
+ // If the width of the input string is now larger than the screen, we nee to
+ // adjust the index to start printing portions of the string
+ //
+ SetUnicodeMem (BufferedString, LargestString, L' ');
+
+ PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
+
+ if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
+ Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
+ } else {
+ Index = 0;
+ }
+
+ for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
+ BufferedString[Count] = StringBuffer[Index];
+ }
+
+ PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
+ break;
+ }
+ } while (!SelectionComplete);
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ return EFI_SUCCESS;
+}
+
+/**
+ Draw a pop up windows based on the dimension, number of lines and
+ strings specified.
+
+ @param RequestedWidth The width of the pop-up.
+ @param NumberOfLines The number of lines.
+ @param Marker The variable argument list for the list of string to be printed.
+
+**/
+VOID
+CreateSharedPopUp (
+ IN UINTN RequestedWidth,
+ IN UINTN NumberOfLines,
+ IN VA_LIST Marker
+ )
+{
+ UINTN Index;
+ UINTN Count;
+ CHAR16 Character;
+ UINTN Start;
+ UINTN End;
+ UINTN Top;
+ UINTN Bottom;
+ CHAR16 *String;
+ UINTN DimensionsWidth;
+ UINTN DimensionsHeight;
+
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
+
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+
+ if ((RequestedWidth + 2) > DimensionsWidth) {
+ RequestedWidth = DimensionsWidth - 2;
+ }
+
+ //
+ // Subtract the PopUp width from total Columns, allow for one space extra on
+ // each end plus a border.
+ //
+ Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
+ End = Start + RequestedWidth + 1;
+
+ Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
+ Bottom = Top + NumberOfLines + 2;
+
+ Character = BOXDRAW_DOWN_RIGHT;
+ PrintCharAt (Start, Top, Character);
+ Character = BOXDRAW_HORIZONTAL;
+ for (Index = Start; Index + 2 < End; Index++) {
+ PrintChar (Character);
+ }
+
+ Character = BOXDRAW_DOWN_LEFT;
+ PrintChar (Character);
+ Character = BOXDRAW_VERTICAL;
+
+ Count = 0;
+ for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
+ String = VA_ARG (Marker, CHAR16*);
+
+ //
+ // This will clear the background of the line - we never know who might have been
+ // here before us. This differs from the next clear in that it used the non-reverse
+ // video for normal printing.
+ //
+ if (GetStringWidth (String) / 2 > 1) {
+ ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
+ }
+
+ //
+ // Passing in a space results in the assumption that this is where typing will occur
+ //
+ if (String[0] == L' ') {
+ ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
+ }
+
+ //
+ // Passing in a NULL results in a blank space
+ //
+ if (String[0] == CHAR_NULL) {
+ ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
+ }
+
+ PrintStringAt (
+ ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
+ Index + 1,
+ String
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
+ PrintCharAt (Start, Index + 1, Character);
+ PrintCharAt (End - 1, Index + 1, Character);
+ }
+
+ Character = BOXDRAW_UP_RIGHT;
+ PrintCharAt (Start, Bottom - 1, Character);
+ Character = BOXDRAW_HORIZONTAL;
+ for (Index = Start; Index + 2 < End; Index++) {
+ PrintChar (Character);
+ }
+
+ Character = BOXDRAW_UP_LEFT;
+ PrintChar (Character);
+}
+
+/**
+ Draw a pop up windows based on the dimension, number of lines and
+ strings specified.
+
+ @param RequestedWidth The width of the pop-up.
+ @param NumberOfLines The number of lines.
+ @param ... A series of text strings that displayed in the pop-up.
+
+**/
+VOID
+EFIAPI
+CreateMultiStringPopUp (
+ IN UINTN RequestedWidth,
+ IN UINTN NumberOfLines,
+ ...
+ )
+{
+ VA_LIST Marker;
+
+ VA_START (Marker, NumberOfLines);
+
+ CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
+
+ VA_END (Marker);
+}
+
+
+/**
+ Update status bar on the bottom of menu.
+
+ @param Selection Current Selction info.
+ @param MessageType The type of message to be shown.
+ @param Flags The flags in Question header.
+ @param State Set or clear.
+
+**/
+VOID
+UpdateStatusBar (
+ IN UI_MENU_SELECTION *Selection,
+ IN UINTN MessageType,
+ IN UINT8 Flags,
+ IN BOOLEAN State
+ )
+{
+ UINTN Index;
+ CHAR16 *NvUpdateMessage;
+ CHAR16 *InputErrorMessage;
+ LIST_ENTRY *Link;
+ FORM_BROWSER_FORMSET *LocalFormSet;
+ FORM_BROWSER_STATEMENT *Question;
+
+ NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
+ InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
+
+ switch (MessageType) {
+ case INPUT_ERROR:
+ if (State) {
+ gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
+ PrintStringAt (
+ gScreenDimensions.LeftColumn + gPromptBlockWidth,
+ gScreenDimensions.BottomRow - 1,
+ InputErrorMessage
+ );
+ mInputError = TRUE;
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor));
+ for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
+ PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" ");
+ }
+
+ mInputError = FALSE;
+ }
+ break;
+
+ case NV_UPDATE_REQUIRED:
+ //
+ // Global setting support. Show configuration change on every form.
+ //
+ if (State) {
+ gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
+
+ if (Selection != NULL && Selection->Statement != NULL) {
+ Question = Selection->Statement;
+ if (Question->Storage != NULL || Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) {
+ //
+ // Update only for Question value that need to be saved into Storage.
+ //
+ Selection->Form->NvUpdateRequired = TRUE;
+ }
+ }
+
+ if (Selection == NULL || IsNvUpdateRequired (Selection->FormSet)) {
+ gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
+ PrintStringAt (
+ gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
+ gScreenDimensions.BottomRow - 1,
+ NvUpdateMessage
+ );
+ }
+ } else {
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor));
+ for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
+ PrintAt (
+ (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
+ gScreenDimensions.BottomRow - 1,
+ L" "
+ );
+ }
+ }
+ break;
+
+ case REFRESH_STATUS_BAR:
+ if (mInputError) {
+ UpdateStatusBar (Selection, INPUT_ERROR, Flags, TRUE);
+ }
+
+ switch (gBrowserSettingScope) {
+ case SystemLevel:
+ //
+ // Check the maintain list to see whether there is any change.
+ //
+ Link = GetFirstNode (&gBrowserFormSetList);
+ while (!IsNull (&gBrowserFormSetList, Link)) {
+ LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link);
+ if (IsNvUpdateRequired(LocalFormSet)) {
+ UpdateStatusBar (NULL, NV_UPDATE_REQUIRED, Flags, TRUE);
+ break;
+ }
+ Link = GetNextNode (&gBrowserFormSetList, Link);
+ }
+ break;
+ case FormSetLevel:
+ case FormLevel:
+ UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Flags, TRUE);
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ FreePool (InputErrorMessage);
+ FreePool (NvUpdateMessage);
+ return ;
+}
+
+
+/**
+ Get the supported width for a particular op-code
+
+ @param Statement The FORM_BROWSER_STATEMENT structure passed in.
+ @param Handle The handle in the HII database being used
+
+ @return Returns the number of CHAR16 characters that is support.
+
+**/
+UINT16
+GetWidth (
+ IN FORM_BROWSER_STATEMENT *Statement,
+ IN EFI_HII_HANDLE Handle
+ )
+{
+ CHAR16 *String;
+ UINTN Size;
+ UINT16 Width;
+
+ Size = 0;
+
+ //
+ // See if the second text parameter is really NULL
+ //
+ if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
+ String = GetToken (Statement->TextTwo, Handle);
+ Size = StrLen (String);
+ FreePool (String);
+ }
+
+ if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
+ (Statement->Operand == EFI_IFR_REF_OP) ||
+ (Statement->Operand == EFI_IFR_PASSWORD_OP) ||
+ (Statement->Operand == EFI_IFR_ACTION_OP) ||
+ (Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
+ //
+ // Allow a wide display if text op-code and no secondary text op-code
+ //
+ ((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
+ ) {
+ Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
+ } else {
+ Width = (UINT16) gPromptBlockWidth;
+ }
+
+ if (Statement->InSubtitle) {
+ Width -= SUBTITLE_INDENT;
+ }
+
+ return (UINT16) (Width - LEFT_SKIPPED_COLUMNS);
+}
+
+/**
+ Will copy LineWidth amount of a string in the OutputString buffer and return the
+ number of CHAR16 characters that were copied into the OutputString buffer.
+ The output string format is:
+ Glyph Info + String info + '\0'.
+
+ In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
+
+ @param InputString String description for this option.
+ @param LineWidth Width of the desired string to extract in CHAR16
+ characters
+ @param GlyphWidth The glyph width of the begin of the char in the string.
+ @param Index Where in InputString to start the copy process
+ @param OutputString Buffer to copy the string into
+
+ @return Returns the number of CHAR16 characters that were copied into the OutputString
+ buffer, include extra glyph info and '\0' info.
+
+**/
+UINT16
+GetLineByWidth (
+ IN CHAR16 *InputString,
+ IN UINT16 LineWidth,
+ IN OUT UINT16 *GlyphWidth,
+ IN OUT UINTN *Index,
+ OUT CHAR16 **OutputString
+ )
+{
+ UINT16 StrOffset;
+ UINT16 GlyphOffset;
+ UINT16 OriginalGlyphWidth;
+ BOOLEAN ReturnFlag;
+ UINT16 LastSpaceOffset;
+ UINT16 LastGlyphWidth;
+
+ if (InputString == NULL || Index == NULL || OutputString == NULL) {
+ return 0;
+ }
+
+ if (LineWidth == 0 || *GlyphWidth == 0) {
+ return 0;
+ }
+
+ //
+ // Save original glyph width.
+ //
+ OriginalGlyphWidth = *GlyphWidth;
+ LastGlyphWidth = OriginalGlyphWidth;
+ ReturnFlag = FALSE;
+ LastSpaceOffset = 0;
+
+ //
+ // NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen.
+ // To avoid displaying this empty line in screen, just skip the two CHARs here.
+ //
+ if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
+ *Index = *Index + 2;
+ }
+
+ //
+ // Fast-forward the string and see if there is a carriage-return in the string
+ //
+ for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) {
+ switch (InputString[*Index + StrOffset]) {
+ case NARROW_CHAR:
+ *GlyphWidth = 1;
+ break;
+
+ case WIDE_CHAR:
+ *GlyphWidth = 2;
+ break;
+
+ case CHAR_CARRIAGE_RETURN:
+ case CHAR_LINEFEED:
+ case CHAR_NULL:
+ ReturnFlag = TRUE;
+ break;
+
+ default:
+ GlyphOffset = GlyphOffset + *GlyphWidth;
+
+ //
+ // Record the last space info in this line. Will be used in rewind.
+ //
+ if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) {
+ LastSpaceOffset = StrOffset;
+ LastGlyphWidth = *GlyphWidth;
+ }
+ break;
+ }
+
+ if (ReturnFlag) {
+ break;
+ }
+ }
+
+ //
+ // Rewind the string from the maximum size until we see a space to break the line
+ //
+ if (GlyphOffset > LineWidth) {
+ //
+ // Rewind the string to last space char in this line.
+ //
+ if (LastSpaceOffset != 0) {
+ StrOffset = LastSpaceOffset;
+ *GlyphWidth = LastGlyphWidth;
+ } else {
+ //
+ // Roll back to last char in the line width.
+ //
+ StrOffset--;
+ }
+ }
+
+ //
+ // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
+ //
+ if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
+ return 0;
+ }
+
+ //
+ // Need extra glyph info and '\0' info, so +2.
+ //
+ *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16)));
+ if (*OutputString == NULL) {
+ return 0;
+ }
+
+ //
+ // Save the glyph info at the begin of the string, will used by Print function.
+ //
+ if (OriginalGlyphWidth == 1) {
+ *(*OutputString) = NARROW_CHAR;
+ } else {
+ *(*OutputString) = WIDE_CHAR;
+ }
+
+ CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16));
+
+ if (InputString[*Index + StrOffset] == CHAR_SPACE) {
+ //
+ // Skip the space info at the begin of next line.
+ //
+ *Index = (UINT16) (*Index + StrOffset + 1);
+ } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
+ //
+ // Skip the /n or /n/r info.
+ //
+ if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
+ *Index = (UINT16) (*Index + StrOffset + 2);
+ } else {
+ *Index = (UINT16) (*Index + StrOffset + 1);
+ }
+ } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
+ //
+ // Skip the /r or /r/n info.
+ //
+ if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
+ *Index = (UINT16) (*Index + StrOffset + 2);
+ } else {
+ *Index = (UINT16) (*Index + StrOffset + 1);
+ }
+ } else {
+ *Index = (UINT16) (*Index + StrOffset);
+ }
+
+ //
+ // Include extra glyph info and '\0' info, so +2.
+ //
+ return StrOffset + 2;
+}
+
+
+/**
+ Update display lines for a Menu Option.
+
+ @param Selection The user's selection.
+ @param MenuOption The MenuOption to be checked.
+
+**/
+VOID
+UpdateOptionSkipLines (
+ IN UI_MENU_SELECTION *Selection,
+ IN UI_MENU_OPTION *MenuOption
+ )
+{
+ UINTN Index;
+ UINT16 Width;
+ UINTN Row;
+ UINTN OriginalRow;
+ CHAR16 *OutputString;
+ CHAR16 *OptionString;
+ UINT16 GlyphWidth;
+
+ Row = 0;
+ OptionString = NULL;
+ Width = (UINT16) gOptionBlockWidth;
+ OriginalRow = 0;
+ GlyphWidth = 1;
+
+ ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
+ if (OptionString == NULL) {
+ return;
+ }
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index]) != 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+
+ FreePool (OutputString);
+ }
+
+ if (OptionString != NULL) {
+ FreePool (OptionString);
+ }
+}
+
+
+/**
+ Check whether this Menu Option could be highlighted.
+
+ This is an internal function.
+
+ @param MenuOption The MenuOption to be checked.
+
+ @retval TRUE This Menu Option is selectable.
+ @retval FALSE This Menu Option could not be selected.
+
+**/
+BOOLEAN
+IsSelectable (
+ UI_MENU_OPTION *MenuOption
+ )
+{
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) ||
+ MenuOption->GrayOut || MenuOption->ReadOnly) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+
+/**
+ Determine if the menu is the last menu that can be selected.
+
+ This is an internal function.
+
+ @param Direction The scroll direction. False is down. True is up.
+ @param CurrentPos The current focus.
+
+ @return FALSE -- the menu isn't the last menu that can be selected.
+ @return TRUE -- the menu is the last menu that can be selected.
+
+**/
+BOOLEAN
+ValueIsScroll (
+ IN BOOLEAN Direction,
+ IN LIST_ENTRY *CurrentPos
+ )
+{
+ LIST_ENTRY *Temp;
+
+ Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
+
+ if (Temp == &gMenuOption) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Move to next selectable statement.
+
+ This is an internal function.
+
+ @param Selection Menu selection.
+ @param GoUp The navigation direction. TRUE: up, FALSE: down.
+ @param CurrentPosition Current position.
+ @param GapToTop Gap position to top or bottom.
+
+ @return The row distance from current MenuOption to next selectable MenuOption.
+
+ @retval -1 Reach the begin of the menu, still can't find the selectable menu.
+ @retval Value Find the selectable menu, maybe the truly selectable, maybe the l
+ last menu showing at current form.
+
+**/
+INTN
+MoveToNextStatement (
+ IN UI_MENU_SELECTION *Selection,
+ IN BOOLEAN GoUp,
+ IN OUT LIST_ENTRY **CurrentPosition,
+ IN UINTN GapToTop
+ )
+{
+ INTN Distance;
+ LIST_ENTRY *Pos;
+ UI_MENU_OPTION *NextMenuOption;
+ UI_MENU_OPTION *PreMenuOption;
+
+ Distance = 0;
+ Pos = *CurrentPosition;
+ PreMenuOption = MENU_OPTION_FROM_LINK (Pos);
+
+ while (TRUE) {
+ NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
+ //
+ // NextMenuOption->Row == 0 means this menu has not calculate
+ // the NextMenuOption->Skip value yet, just calculate here.
+ //
+ if (NextMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, NextMenuOption);
+ }
+
+ if (GoUp && (PreMenuOption != NextMenuOption)) {
+ //
+ // In this case, still can't find the selectable menu,
+ // return the last one in the showing form.
+ //
+ if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
+ NextMenuOption = PreMenuOption;
+ break;
+ }
+
+ //
+ // Current Position doesn't need to be caculated when go up.
+ // Caculate distanct at first when go up
+ //
+ Distance += NextMenuOption->Skip;
+ }
+
+ if (IsSelectable (NextMenuOption)) {
+ break;
+ }
+
+ //
+ // Arrive at begin of the menu list.
+ //
+ if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
+ Distance = -1;
+ break;
+ }
+
+ if (!GoUp) {
+ //
+ // In this case, still can't find the selectable menu,
+ // return the last one in the showing form.
+ //
+ if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
+ NextMenuOption = PreMenuOption;
+ break;
+ }
+
+ Distance += NextMenuOption->Skip;
+ }
+
+ PreMenuOption = NextMenuOption;
+ Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
+ }
+
+ *CurrentPosition = &NextMenuOption->Link;
+ return Distance;
+}
+
+
+/**
+ Adjust Data and Time position accordingly.
+ Data format : [01/02/2004] [11:22:33]
+ Line number : 0 0 1 0 0 1
+
+ This is an internal function.
+
+ @param DirectionUp the up or down direction. False is down. True is
+ up.
+ @param CurrentPosition Current position. On return: Point to the last
+ Option (Year or Second) if up; Point to the first
+ Option (Month or Hour) if down.
+
+ @return Return line number to pad. It is possible that we stand on a zero-advance
+ @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
+
+**/
+UINTN
+AdjustDateAndTimePosition (
+ IN BOOLEAN DirectionUp,
+ IN OUT LIST_ENTRY **CurrentPosition
+ )
+{
+ UINTN Count;
+ LIST_ENTRY *NewPosition;
+ UI_MENU_OPTION *MenuOption;
+ UINTN PadLineNumber;
+
+ PadLineNumber = 0;
+ NewPosition = *CurrentPosition;
+ MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
+
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ //
+ // Calculate the distance from current position to the last Date/Time MenuOption
+ //
+ Count = 0;
+ while (MenuOption->Skip == 0) {
+ Count++;
+ NewPosition = NewPosition->ForwardLink;
+ MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
+ PadLineNumber = 1;
+ }
+
+ NewPosition = *CurrentPosition;
+ if (DirectionUp) {
+ //
+ // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
+ // to be one that back to the previous set of MenuOptions, we need to advance to the first
+ // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
+ // checking can be done.
+ //
+ while (Count++ < 2) {
+ NewPosition = NewPosition->BackLink;
+ }
+ } else {
+ //
+ // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
+ // to be one that progresses to the next set of MenuOptions, we need to advance to the last
+ // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
+ // checking can be done.
+ //
+ while (Count-- > 0) {
+ NewPosition = NewPosition->ForwardLink;
+ }
+ }
+
+ *CurrentPosition = NewPosition;
+ }
+
+ return PadLineNumber;
+}
+
+/**
+ Find HII Handle in the HII database associated with given Device Path.
+
+ If DevicePath is NULL, then ASSERT.
+
+ @param DevicePath Device Path associated with the HII package list
+ handle.
+
+ @retval Handle HII package list Handle associated with the Device
+ Path.
+ @retval NULL Hii Package list handle is not found.
+
+**/
+EFI_HII_HANDLE
+EFIAPI
+DevicePathToHiiHandle (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
+ UINTN BufferSize;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_HANDLE Handle;
+ EFI_HANDLE DriverHandle;
+ EFI_HII_HANDLE *HiiHandles;
+ EFI_HII_HANDLE HiiHandle;
+
+ ASSERT (DevicePath != NULL);
+
+ TmpDevicePath = DevicePath;
+ //
+ // Locate Device Path Protocol handle buffer
+ //
+ Status = gBS->LocateDevicePath (
+ &gEfiDevicePathProtocolGuid,
+ &TmpDevicePath,
+ &DriverHandle
+ );
+ if (EFI_ERROR (Status) || !IsDevicePathEnd (TmpDevicePath)) {
+ return NULL;
+ }
+
+ //
+ // Retrieve all HII Handles from HII database
+ //
+ BufferSize = 0x1000;
+ HiiHandles = AllocatePool (BufferSize);
+ ASSERT (HiiHandles != NULL);
+ Status = mHiiDatabase->ListPackageLists (
+ mHiiDatabase,
+ EFI_HII_PACKAGE_TYPE_ALL,
+ NULL,
+ &BufferSize,
+ HiiHandles
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ FreePool (HiiHandles);
+ HiiHandles = AllocatePool (BufferSize);
+ ASSERT (HiiHandles != NULL);
+
+ Status = mHiiDatabase->ListPackageLists (
+ mHiiDatabase,
+ EFI_HII_PACKAGE_TYPE_ALL,
+ NULL,
+ &BufferSize,
+ HiiHandles
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ FreePool (HiiHandles);
+ return NULL;
+ }
+
+ //
+ // Search Hii Handle by Driver Handle
+ //
+ HiiHandle = NULL;
+ HandleCount = BufferSize / sizeof (EFI_HII_HANDLE);
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = mHiiDatabase->GetPackageListHandle (
+ mHiiDatabase,
+ HiiHandles[Index],
+ &Handle
+ );
+ if (!EFI_ERROR (Status) && (Handle == DriverHandle)) {
+ HiiHandle = HiiHandles[Index];
+ break;
+ }
+ }
+
+ FreePool (HiiHandles);
+ return HiiHandle;
+}
+
+/**
+ Find HII Handle in the HII database associated with given form set guid.
+
+ If FormSetGuid is NULL, then ASSERT.
+
+ @param ComparingGuid FormSet Guid associated with the HII package list
+ handle.
+
+ @retval Handle HII package list Handle associated with the Device
+ Path.
+ @retval NULL Hii Package list handle is not found.
+
+**/
+EFI_HII_HANDLE
+FormSetGuidToHiiHandle (
+ EFI_GUID *ComparingGuid
+ )
+{
+ EFI_HII_HANDLE *HiiHandles;
+ UINTN Index;
+ EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList;
+ UINTN BufferSize;
+ UINT32 Offset;
+ UINT32 Offset2;
+ UINT32 PackageListLength;
+ EFI_HII_PACKAGE_HEADER PackageHeader;
+ UINT8 *Package;
+ UINT8 *OpCodeData;
+ EFI_STATUS Status;
+ EFI_HII_HANDLE HiiHandle;
+
+ ASSERT (ComparingGuid != NULL);
+
+ HiiHandle = NULL;
+ //
+ // Get all the Hii handles
+ //
+ HiiHandles = HiiGetHiiHandles (NULL);
+ ASSERT (HiiHandles != NULL);
+
+ //
+ // Search for formset of each class type
+ //
+ for (Index = 0; HiiHandles[Index] != NULL; Index++) {
+ BufferSize = 0;
+ HiiPackageList = NULL;
+ Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ HiiPackageList = AllocatePool (BufferSize);
+ ASSERT (HiiPackageList != NULL);
+
+ Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList);
+ }
+ if (EFI_ERROR (Status) || HiiPackageList == NULL) {
+ return NULL;
+ }
+
+ //
+ // Get Form package from this HII package List
+ //
+ Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER);
+ Offset2 = 0;
+ CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32));
+
+ while (Offset < PackageListLength) {
+ Package = ((UINT8 *) HiiPackageList) + Offset;
+ CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER));
+
+ if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) {
+ //
+ // Search FormSet in this Form Package
+ //
+ Offset2 = sizeof (EFI_HII_PACKAGE_HEADER);
+ while (Offset2 < PackageHeader.Length) {
+ OpCodeData = Package + Offset2;
+
+ if (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) {
+ //
+ // Try to compare against formset GUID
+ //
+ if (CompareGuid (ComparingGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) {
+ HiiHandle = HiiHandles[Index];
+ break;
+ }
+ }
+
+ Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length;
+ }
+ }
+ if (HiiHandle != NULL) {
+ break;
+ }
+ Offset += PackageHeader.Length;
+ }
+
+ FreePool (HiiPackageList);
+ if (HiiHandle != NULL) {
+ break;
+ }
+ }
+
+ FreePool (HiiHandles);
+
+ return HiiHandle;
+}
+
+/**
+ Process the goto op code, update the info in the selection structure.
+
+ @param Statement The statement belong to goto op code.
+ @param Selection The selection info.
+ @param Repaint Whether need to repaint the menu.
+ @param NewLine Whether need to create new line.
+
+ @retval EFI_SUCCESS The menu process successfully.
+ @return Other value if the process failed.
+**/
+EFI_STATUS
+ProcessGotoOpCode (
+ IN OUT FORM_BROWSER_STATEMENT *Statement,
+ IN OUT UI_MENU_SELECTION *Selection,
+ OUT BOOLEAN *Repaint,
+ OUT BOOLEAN *NewLine
+ )
+{
+ CHAR16 *StringPtr;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ FORM_BROWSER_FORM *RefForm;
+ EFI_INPUT_KEY Key;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ StringPtr = NULL;
+
+ //
+ // Prepare the device path check, get the device path info first.
+ //
+ if (Statement->HiiValue.Value.ref.DevicePath != 0) {
+ StringPtr = GetToken (Statement->HiiValue.Value.ref.DevicePath, Selection->FormSet->HiiHandle);
+ }
+
+ //
+ // Check whether the device path string is a valid string.
+ //
+ if (Statement->HiiValue.Value.ref.DevicePath != 0 && StringPtr != NULL) {
+ if (Selection->Form->ModalForm) {
+ return Status;
+ }
+
+ //
+ // Goto another Hii Package list
+ //
+ if (mPathFromText != NULL) {
+ DevicePath = mPathFromText->ConvertTextToDevicePath(StringPtr);
+ if (DevicePath != NULL) {
+ Selection->Handle = DevicePathToHiiHandle (DevicePath);
+ FreePool (DevicePath);
+ }
+ FreePool (StringPtr);
+ } else {
+ //
+ // Not found the EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol.
+ //
+ do {
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gProtocolNotFound, gPressEnter, gEmptyString);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ if (Repaint != NULL) {
+ *Repaint = TRUE;
+ }
+ FreePool (StringPtr);
+ return Status;
+ }
+
+ Selection->Action = UI_ACTION_REFRESH_FORMSET;
+ if (Selection->Handle == NULL) {
+ //
+ // If target Hii Handle not found, exit
+ //
+ Selection->Action = UI_ACTION_EXIT;
+ Selection->Statement = NULL;
+ return Status;
+ }
+
+ CopyMem (&Selection->FormSetGuid,&Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
+ Selection->FormId = Statement->HiiValue.Value.ref.FormId;
+ Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
+ } else if (!CompareGuid (&Statement->HiiValue.Value.ref.FormSetGuid, &gZeroGuid)) {
+ if (Selection->Form->ModalForm) {
+ return Status;
+ }
+ //
+ // Goto another Formset, check for uncommitted data
+ //
+ Selection->Action = UI_ACTION_REFRESH_FORMSET;
+
+ Selection->Handle = FormSetGuidToHiiHandle(&Statement->HiiValue.Value.ref.FormSetGuid);
+ if (Selection->Handle == NULL) {
+ //
+ // If target Hii Handle not found, exit
+ //
+ Selection->Action = UI_ACTION_EXIT;
+ Selection->Statement = NULL;
+ return Status;
+ }
+
+ CopyMem (&Selection->FormSetGuid, &Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
+ Selection->FormId = Statement->HiiValue.Value.ref.FormId;
+ Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
+ } else if (Statement->HiiValue.Value.ref.FormId != 0) {
+ //
+ // Check whether target From is suppressed.
+ //
+ RefForm = IdToForm (Selection->FormSet, Statement->HiiValue.Value.ref.FormId);
+
+ if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) {
+ if (EvaluateExpressionList(RefForm->SuppressExpression, TRUE, Selection->FormSet, RefForm) != ExpressFalse) {
+ //
+ // Form is suppressed.
+ //
+ do {
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ if (Repaint != NULL) {
+ *Repaint = TRUE;
+ }
+ return Status;
+ }
+ }
+
+ //
+ // Goto another form inside this formset,
+ //
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+
+ Selection->FormId = Statement->HiiValue.Value.ref.FormId;
+ Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
+ } else if (Statement->HiiValue.Value.ref.QuestionId != 0) {
+ //
+ // Goto another Question
+ //
+ Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
+
+ if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ } else {
+ if (Repaint != NULL) {
+ *Repaint = TRUE;
+ }
+ if (NewLine != NULL) {
+ *NewLine = TRUE;
+ }
+ }
+ } else {
+ if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Display menu and wait for user to select one menu option, then return it.
+ If AutoBoot is enabled, then if user doesn't select any option,
+ after period of time, it will automatically return the first menu option.
+
+ @param Selection Menu selection.
+
+ @retval EFI_SUCESSS This function always return successfully for now.
+
+**/
+EFI_STATUS
+UiDisplayMenu (
+ IN OUT UI_MENU_SELECTION *Selection
+ )
+{
+ INTN SkipValue;
+ INTN Difference;
+ UINTN DistanceValue;
+ UINTN Row;
+ UINTN Col;
+ UINTN Temp;
+ UINTN Temp2;
+ UINTN TopRow;
+ UINTN BottomRow;
+ UINTN OriginalRow;
+ UINTN Index;
+ UINT16 Width;
+ CHAR16 *StringPtr;
+ CHAR16 *OptionString;
+ CHAR16 *OutputString;
+ CHAR16 *HelpString;
+ CHAR16 *HelpHeaderString;
+ CHAR16 *HelpBottomString;
+ BOOLEAN NewLine;
+ BOOLEAN Repaint;
+ BOOLEAN SavedValue;
+ BOOLEAN UpArrow;
+ BOOLEAN DownArrow;
+ BOOLEAN InitializedFlag;
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ LIST_ENTRY *Link;
+ LIST_ENTRY *NewPos;
+ LIST_ENTRY *TopOfScreen;
+ LIST_ENTRY *SavedListEntry;
+ UI_MENU_OPTION *MenuOption;
+ UI_MENU_OPTION *NextMenuOption;
+ UI_MENU_OPTION *SavedMenuOption;
+ UI_MENU_OPTION *PreviousMenuOption;
+ UI_CONTROL_FLAG ControlFlag;
+ EFI_SCREEN_DESCRIPTOR LocalScreen;
+ MENU_REFRESH_ENTRY *MenuRefreshEntry;
+ MENU_REFRESH_ENTRY *MenuUpdateEntry;
+ UI_SCREEN_OPERATION ScreenOperation;
+ UINT8 MinRefreshInterval;
+ UINT16 DefaultId;
+ FORM_BROWSER_STATEMENT *Statement;
+ UI_MENU_LIST *CurrentMenu;
+ UINTN ModalSkipColumn;
+ BROWSER_HOT_KEY *HotKey;
+ UINTN HelpPageIndex;
+ UINTN HelpPageCount;
+ UINTN RowCount;
+ UINTN HelpLine;
+ UINTN HelpHeaderLine;
+ UINTN HelpBottomLine;
+ BOOLEAN MultiHelpPage;
+ UINT16 GlyphWidth;
+ UINT16 EachLineWidth;
+ UINT16 HeaderLineWidth;
+ UINT16 BottomLineWidth;
+
+ CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
+
+ Status = EFI_SUCCESS;
+ HelpString = NULL;
+ HelpHeaderString = NULL;
+ HelpBottomString = NULL;
+ OptionString = NULL;
+ ScreenOperation = UiNoOperation;
+ NewLine = TRUE;
+ MinRefreshInterval = 0;
+ DefaultId = 0;
+ HelpPageCount = 0;
+ HelpLine = 0;
+ RowCount = 0;
+ HelpBottomLine = 0;
+ HelpHeaderLine = 0;
+ HelpPageIndex = 0;
+ MultiHelpPage = FALSE;
+ EachLineWidth = 0;
+ HeaderLineWidth = 0;
+ BottomLineWidth = 0;
+ OutputString = NULL;
+ UpArrow = FALSE;
+ DownArrow = FALSE;
+ SkipValue = 0;
+ MenuRefreshEntry = gMenuRefreshHead;
+
+ NextMenuOption = NULL;
+ PreviousMenuOption = NULL;
+ SavedMenuOption = NULL;
+ HotKey = NULL;
+ ModalSkipColumn = (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 6;
+
+ ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
+
+ if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE){
+ TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ } else {
+ TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
+ }
+
+ if (Selection->Form->ModalForm) {
+ Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS + ModalSkipColumn;
+ } else {
+ Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
+ }
+
+ BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - SCROLL_ARROW_HEIGHT - 1;
+
+ Selection->TopRow = TopRow;
+ Selection->BottomRow = BottomRow;
+ Selection->PromptCol = Col;
+ Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
+ Selection->Statement = NULL;
+
+ TopOfScreen = gMenuOption.ForwardLink;
+ Repaint = TRUE;
+ MenuOption = NULL;
+
+ //
+ // Find current Menu
+ //
+ CurrentMenu = UiFindMenuList (Selection->Handle, &Selection->FormSetGuid, Selection->FormId);
+ if (CurrentMenu == NULL) {
+ //
+ // Current menu not found, add it to the menu tree
+ //
+ CurrentMenu = UiAddMenuList (Selection->CurrentMenu, Selection->Handle, &Selection->FormSetGuid, Selection->FormId);
+ }
+ ASSERT (CurrentMenu != NULL);
+ Selection->CurrentMenu = CurrentMenu;
+
+ if (Selection->QuestionId == 0) {
+ //
+ // Highlight not specified, fetch it from cached menu
+ //
+ Selection->QuestionId = CurrentMenu->QuestionId;
+ Selection->Sequence = CurrentMenu->Sequence;
+ }
+
+ //
+ // Init option as the current user's selection
+ //
+ InitializedFlag = TRUE;
+ NewPos = gMenuOption.ForwardLink;
+
+ gST->ConOut->EnableCursor (gST->ConOut, FALSE);
+ UpdateStatusBar (Selection, REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
+
+ ControlFlag = CfInitialization;
+ Selection->Action = UI_ACTION_NONE;
+ while (TRUE) {
+ switch (ControlFlag) {
+ case CfInitialization:
+ if (IsListEmpty (&gMenuOption)) {
+ ControlFlag = CfReadKey;
+ } else {
+ ControlFlag = CfCheckSelection;
+ }
+ break;
+
+ case CfCheckSelection:
+ if (Selection->Action != UI_ACTION_NONE) {
+ ControlFlag = CfExit;
+ } else {
+ ControlFlag = CfRepaint;
+ }
+ break;
+
+ case CfRepaint:
+ ControlFlag = CfRefreshHighLight;
+
+ if (Repaint) {
+ //
+ // Display menu
+ //
+ DownArrow = FALSE;
+ UpArrow = FALSE;
+ Row = TopRow;
+
+ Temp = (UINTN) SkipValue;
+ Temp2 = (UINTN) SkipValue;
+
+ //
+ // 1. Clear the screen.
+ //
+ if (Selection->Form->ModalForm) {
+ ClearLines (
+ LocalScreen.LeftColumn + ModalSkipColumn,
+ LocalScreen.LeftColumn + ModalSkipColumn + gPromptBlockWidth + gOptionBlockWidth,
+ TopRow - SCROLL_ARROW_HEIGHT,
+ BottomRow + SCROLL_ARROW_HEIGHT,
+ PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
+ );
+ } else {
+ ClearLines (
+ LocalScreen.LeftColumn,
+ LocalScreen.RightColumn,
+ TopRow - SCROLL_ARROW_HEIGHT,
+ BottomRow + SCROLL_ARROW_HEIGHT,
+ PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
+ );
+ }
+ UiFreeRefreshList ();
+ MinRefreshInterval = 0;
+
+ //
+ // 2.Paint the menu.
+ //
+ for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
+ MenuOption = MENU_OPTION_FROM_LINK (Link);
+ MenuOption->Row = Row;
+ MenuOption->Col = Col;
+ if (Selection->Form->ModalForm) {
+ MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn + ModalSkipColumn;
+ } else {
+ MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
+ }
+
+ Statement = MenuOption->ThisTag;
+ if (Statement->InSubtitle) {
+ MenuOption->Col += SUBTITLE_INDENT;
+ }
+
+ if (MenuOption->GrayOut) {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
+ } else {
+ if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
+ }
+ }
+
+ Width = GetWidth (Statement, MenuOption->Handle);
+ OriginalRow = Row;
+ GlyphWidth = 1;
+
+ if (Statement->Operand == EFI_IFR_REF_OP && MenuOption->Col >= 2) {
+ //
+ // Print Arrow for Goto button.
+ //
+ PrintAt (
+ MenuOption->Col - 2,
+ Row,
+ L"%c",
+ GEOMETRICSHAPE_RIGHT_TRIANGLE
+ );
+ }
+
+ //
+ // 2.1. Paint the description.
+ //
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ //
+ // Temp means need to skip how many lines from the start.
+ //
+ if ((Temp == 0) && (Row <= BottomRow)) {
+ PrintStringAt (MenuOption->Col, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index]) != 0) {
+ if (Temp == 0) {
+ Row++;
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp != 0) {
+ Temp--;
+ }
+ }
+
+ Temp = 0;
+ Row = OriginalRow;
+
+ //
+ // 2.2. Paint the option string.
+ //
+ Status = ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
+ if (EFI_ERROR (Status)) {
+ //
+ // Repaint to clear possible error prompt pop-up
+ //
+ Repaint = TRUE;
+ NewLine = TRUE;
+ ControlFlag = CfRepaint;
+ break;
+ }
+
+ if (OptionString != NULL) {
+ if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
+ ProcessStringForDateTime(MenuOption, OptionString, TRUE);
+ }
+
+ Width = (UINT16) gOptionBlockWidth;
+ OriginalRow = Row;
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp2 == 0) && (Row <= BottomRow)) {
+ PrintStringAt (MenuOption->OptCol, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index]) != 0) {
+ if (Temp2 == 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ Temp2 = 0;
+ Row = OriginalRow;
+
+ FreePool (OptionString);
+ }
+
+ //
+ // 2.4 Special process for Test opcode with test two.
+ //
+ if (!CompareGuid (&Statement->RefreshGuid, &gZeroGuid)) {
+ if (gMenuEventGuidRefreshHead == NULL) {
+ MenuUpdateEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ gMenuEventGuidRefreshHead = MenuUpdateEntry;
+ } else {
+ MenuUpdateEntry = gMenuEventGuidRefreshHead;
+ while (MenuUpdateEntry->Next != NULL) {
+ MenuUpdateEntry = MenuUpdateEntry->Next;
+ }
+ MenuUpdateEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ MenuUpdateEntry = MenuUpdateEntry->Next;
+ }
+ ASSERT (MenuUpdateEntry != NULL);
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, RefreshQuestionNotify, MenuUpdateEntry, &Statement->RefreshGuid, &MenuUpdateEntry->Event);
+ ASSERT (!EFI_ERROR (Status));
+ MenuUpdateEntry->MenuOption = MenuOption;
+ MenuUpdateEntry->Selection = Selection;
+ MenuUpdateEntry->CurrentColumn = MenuOption->OptCol;
+ MenuUpdateEntry->CurrentRow = MenuOption->Row;
+ if (MenuOption->GrayOut) {
+ MenuUpdateEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
+ } else {
+ MenuUpdateEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
+ }
+ }
+
+ //
+ // If Question request refresh, register the op-code
+ //
+ if (Statement->RefreshInterval != 0) {
+ //
+ // Menu will be refreshed at minimal interval of all Questions
+ // which have refresh request
+ //
+ if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
+ MinRefreshInterval = Statement->RefreshInterval;
+ }
+
+ if (gMenuRefreshHead == NULL) {
+ MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ gMenuRefreshHead = MenuRefreshEntry;
+ } else {
+ MenuRefreshEntry = gMenuRefreshHead;
+ while (MenuRefreshEntry->Next != NULL) {
+ MenuRefreshEntry = MenuRefreshEntry->Next;
+ }
+ MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
+ MenuRefreshEntry = MenuRefreshEntry->Next;
+ }
+ ASSERT (MenuRefreshEntry != NULL);
+ MenuRefreshEntry->MenuOption = MenuOption;
+ MenuRefreshEntry->Selection = Selection;
+ MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
+ MenuRefreshEntry->CurrentRow = MenuOption->Row;
+ if (MenuOption->GrayOut) {
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
+ } else {
+ MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
+ }
+ }
+
+ //
+ // If this is a text op with secondary text information
+ //
+ if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
+ StringPtr = GetToken (Statement->TextTwo, MenuOption->Handle);
+
+ Width = (UINT16) gOptionBlockWidth;
+ OriginalRow = Row;
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp == 0) && (Row <= BottomRow)) {
+ PrintStringAt (MenuOption->OptCol, Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&StringPtr[Index]) != 0) {
+ if (Temp2 == 0) {
+ Row++;
+ //
+ // Since the Number of lines for this menu entry may or may not be reflected accurately
+ // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
+ // some testing to ensure we are keeping this in-sync.
+ //
+ // If the difference in rows is greater than or equal to the skip value, increase the skip value
+ //
+ if ((Row - OriginalRow) >= MenuOption->Skip) {
+ MenuOption->Skip++;
+ }
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ Row = OriginalRow;
+ FreePool (StringPtr);
+ }
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+
+ //
+ // 3. Update the row info which will be used by next menu.
+ //
+ if (Link == TopOfScreen) {
+ Row += MenuOption->Skip - SkipValue;
+ } else {
+ Row += MenuOption->Skip;
+ }
+
+ if (Row > BottomRow) {
+ if (!ValueIsScroll (FALSE, Link)) {
+ DownArrow = TRUE;
+ }
+
+ Row = BottomRow + 1;
+ break;
+ }
+ }
+
+ if (!ValueIsScroll (TRUE, TopOfScreen)) {
+ UpArrow = TRUE;
+ }
+
+ if (UpArrow) {
+ gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
+ PrintAt (
+ LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
+ TopRow - SCROLL_ARROW_HEIGHT,
+ L"%c",
+ ARROW_UP
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+ }
+
+ if (DownArrow) {
+ gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
+ PrintAt (
+ LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
+ BottomRow + SCROLL_ARROW_HEIGHT,
+ L"%c",
+ ARROW_DOWN
+ );
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+ }
+
+ MenuOption = NULL;
+ }
+ break;
+
+ case CfRefreshHighLight:
+ //
+ // MenuOption: Last menu option that need to remove hilight
+ // MenuOption is set to NULL in Repaint
+ // NewPos: Current menu option that need to hilight
+ //
+ ControlFlag = CfUpdateHelpString;
+ if (TopOfScreen == &MenuOption->Link) {
+ Temp = SkipValue;
+ } else {
+ Temp = 0;
+ }
+ if (NewPos == TopOfScreen) {
+ Temp2 = SkipValue;
+ } else {
+ Temp2 = 0;
+ }
+ if (InitializedFlag) {
+ InitializedFlag = FALSE;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+ }
+
+ //
+ // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
+ // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
+ //
+ SavedValue = Repaint;
+ Repaint = FALSE;
+
+ if (Selection->QuestionId != 0) {
+ NewPos = gMenuOption.ForwardLink;
+ SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+
+ while ((SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId ||
+ SavedMenuOption->Sequence != Selection->Sequence) &&
+ NewPos->ForwardLink != &gMenuOption) {
+ NewPos = NewPos->ForwardLink;
+ SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ }
+ if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
+ //
+ // Target Question found, find its MenuOption
+ //
+ Link = TopOfScreen;
+
+ for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
+ SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
+ Index += SavedMenuOption->Skip;
+ if (Link == TopOfScreen) {
+ Index -= SkipValue;
+ }
+ Link = Link->ForwardLink;
+ }
+ if (NewPos == Link) {
+ SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
+ }
+
+ //
+ // Not find the selected menu in current show page.
+ // Have two case to enter this if:
+ // 1. Not find the menu at current page.
+ // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
+ // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
+ //
+ // Base on the selected menu will show at the bottom of the page,
+ // select the menu which will show at the top of the page.
+ //
+ if (Link != NewPos || Index > BottomRow ||
+ (Link == NewPos && (SavedMenuOption->Row + SavedMenuOption->Skip - 1 > BottomRow) && (Link != TopOfScreen))) {
+ //
+ // Find the MenuOption which has the skip value for Date/Time opcode.
+ //
+ AdjustDateAndTimePosition(FALSE, &NewPos);
+ //
+ // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
+ //
+ SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ //
+ // SavedMenuOption->Row == 0 means the menu not show yet.
+ //
+ if (SavedMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, SavedMenuOption);
+ }
+
+ //
+ // Base on the selected menu will show at the bottome of next page,
+ // select the menu show at the top of the next page.
+ //
+ Link = NewPos;
+ for (Index = TopRow + SavedMenuOption->Skip; Index <= BottomRow + 1; ) {
+ Link = Link->BackLink;
+ SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
+ if (SavedMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, SavedMenuOption);
+ }
+ Index += SavedMenuOption->Skip;
+ }
+
+ //
+ // Found the menu which will show at the top of the page.
+ //
+ if (Link == NewPos) {
+ //
+ // The menu can show more than one pages, just show the menu at the top of the page.
+ //
+ SkipValue = 0;
+ TopOfScreen = Link;
+ } else {
+ //
+ // Check whether need to skip some line for menu shows at the top of the page.
+ //
+ SkipValue = Index - BottomRow - 1;
+ if (SkipValue > 0 && SkipValue < (INTN) SavedMenuOption->Skip) {
+ TopOfScreen = Link;
+ } else {
+ SkipValue = 0;
+ TopOfScreen = Link->ForwardLink;
+ }
+ }
+
+ Repaint = TRUE;
+ NewLine = TRUE;
+ ControlFlag = CfRepaint;
+ break;
+ }
+ } else {
+ //
+ // Target Question not found, highlight the default menu option
+ //
+ NewPos = TopOfScreen;
+ }
+
+ Selection->QuestionId = 0;
+ }
+
+ if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
+ if (MenuOption != NULL) {
+ //
+ // Remove highlight on last Menu Option
+ //
+ gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
+ ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+ if (OptionString != NULL) {
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
+ (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
+ ) {
+ ProcessStringForDateTime(MenuOption, OptionString, FALSE);
+ }
+
+ Width = (UINT16) gOptionBlockWidth;
+ OriginalRow = MenuOption->Row;
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow)) {
+ PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index]) != 0) {
+ if (Temp == 0) {
+ MenuOption->Row++;
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp != 0) {
+ Temp--;
+ }
+ }
+
+ MenuOption->Row = OriginalRow;
+
+ FreePool (OptionString);
+ } else {
+ if (NewLine) {
+ if (MenuOption->GrayOut) {
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
+ } else if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
+ }
+
+ OriginalRow = MenuOption->Row;
+ Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow)) {
+ PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index]) != 0) {
+ if (Temp == 0) {
+ MenuOption->Row++;
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp != 0) {
+ Temp--;
+ }
+ }
+
+ MenuOption->Row = OriginalRow;
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+ }
+ }
+ }
+
+ //
+ // This is the current selected statement
+ //
+ MenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ Statement = MenuOption->ThisTag;
+ Selection->Statement = Statement;
+ if (!IsSelectable (MenuOption)) {
+ Repaint = SavedValue;
+ UpdateKeyHelp (Selection, MenuOption, FALSE);
+ break;
+ }
+
+ //
+ // Record highlight for current menu
+ //
+ CurrentMenu->QuestionId = Statement->QuestionId;
+ CurrentMenu->Sequence = MenuOption->Sequence;
+
+ //
+ // Set reverse attribute
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));
+ gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
+
+ //
+ // Assuming that we have a refresh linked-list created, lets annotate the
+ // appropriate entry that we are highlighting with its new attribute. Just prior to this
+ // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
+ //
+ if (gMenuRefreshHead != NULL) {
+ for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
+ if (MenuRefreshEntry->MenuOption->GrayOut) {
+ MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
+ } else {
+ MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
+ }
+ if (MenuRefreshEntry->MenuOption == MenuOption) {
+ MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor);
+ }
+ }
+ }
+
+ ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
+ if (OptionString != NULL) {
+ if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
+ ProcessStringForDateTime(MenuOption, OptionString, FALSE);
+ }
+ Width = (UINT16) gOptionBlockWidth;
+
+ OriginalRow = MenuOption->Row;
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp2 == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow) ) {
+ PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&OptionString[Index]) != 0) {
+ if (Temp2 == 0) {
+ MenuOption->Row++;
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ MenuOption->Row = OriginalRow;
+
+ FreePool (OptionString);
+ } else {
+ if (NewLine) {
+ OriginalRow = MenuOption->Row;
+
+ Width = GetWidth (Statement, MenuOption->Handle);
+ GlyphWidth = 1;
+
+ for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
+ if ((Temp2 == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow) ) {
+ PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
+ }
+ //
+ // If there is more string to process print on the next row and increment the Skip value
+ //
+ if (StrLen (&MenuOption->Description[Index]) != 0) {
+ if (Temp2 == 0) {
+ MenuOption->Row++;
+ }
+ }
+
+ FreePool (OutputString);
+ if (Temp2 != 0) {
+ Temp2--;
+ }
+ }
+
+ MenuOption->Row = OriginalRow;
+
+ }
+ }
+
+ UpdateKeyHelp (Selection, MenuOption, FALSE);
+
+ //
+ // Clear reverse attribute
+ //
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
+ }
+ //
+ // Repaint flag will be used when process CfUpdateHelpString, so restore its value
+ // if we didn't break halfway when process CfRefreshHighLight.
+ //
+ Repaint = SavedValue;
+ break;
+
+ case CfUpdateHelpString:
+ ControlFlag = CfPrepareToReadKey;
+ if (Selection->Form->ModalForm) {
+ break;
+ }
+
+ if (Repaint || NewLine) {
+ //
+ // Don't print anything if it is a NULL help token
+ //
+ ASSERT(MenuOption != NULL);
+ if (MenuOption->ThisTag->Help == 0 || !IsSelectable (MenuOption)) {
+ StringPtr = L"\0";
+ } else {
+ StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
+ }
+
+ RowCount = BottomRow - TopRow;
+ HelpPageIndex = 0;
+ //
+ // 1.Calculate how many line the help string need to print.
+ //
+ if (HelpString != NULL) {
+ FreePool (HelpString);
+ }
+ HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount);
+ if (HelpLine > RowCount) {
+ MultiHelpPage = TRUE;
+ StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle);
+ if (HelpHeaderString != NULL) {
+ FreePool (HelpHeaderString);
+ }
+ HelpHeaderLine = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, RowCount);
+ StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle);
+ if (HelpBottomString != NULL) {
+ FreePool (HelpBottomString);
+ }
+ HelpBottomLine = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, RowCount);
+ //
+ // Calculate the help page count.
+ //
+ if (HelpLine > 2 * RowCount - 2) {
+ HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1;
+ if ((HelpLine - RowCount + 1) % (RowCount - 2) > 1) {
+ HelpPageCount += 1;
+ }
+ } else {
+ HelpPageCount = 2;
+ }
+ } else {
+ MultiHelpPage = FALSE;
+ }
+ }
+
+ //
+ // Clean the help field first.
+ //
+ ClearLines (
+ LocalScreen.RightColumn - gHelpBlockWidth,
+ LocalScreen.RightColumn,
+ TopRow,
+ BottomRow,
+ PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
+ );
+
+ //
+ // Check whether need to show the 'More(U/u)' at the begin.
+ // Base on current direct info, here shows aligned to the right side of the column.
+ // If the direction is multi line and aligned to right side may have problem, so
+ // add ASSERT code here.
+ //
+ if (HelpPageIndex > 0) {
+ gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT | FIELD_BACKGROUND);
+ for (Index = 0; Index < HelpHeaderLine; Index++) {
+ ASSERT (HelpHeaderLine == 1);
+ ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1));
+ PrintStringAt (
+ LocalScreen.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1,
+ Index + TopRow,
+ &HelpHeaderString[Index * HeaderLineWidth]
+ );
+ }
+ }
+
+ gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
+ //
+ // Print the help string info.
+ //
+ if (!MultiHelpPage) {
+ for (Index = 0; Index < HelpLine; Index++) {
+ PrintStringAt (
+ LocalScreen.RightColumn - gHelpBlockWidth,
+ Index + TopRow,
+ &HelpString[Index * EachLineWidth]
+ );
+ }
+ gST->ConOut->SetCursorPosition(gST->ConOut, LocalScreen.RightColumn-1, BottomRow);
+ } else {
+ if (HelpPageIndex == 0) {
+ for (Index = 0; Index < RowCount - HelpBottomLine; Index++) {
+ PrintStringAt (
+ LocalScreen.RightColumn - gHelpBlockWidth,
+ Index + TopRow,
+ &HelpString[Index * EachLineWidth]
+ );
+ }
+ } else {
+ for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) &&
+ (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) {
+ PrintStringAt (
+ LocalScreen.RightColumn - gHelpBlockWidth,
+ Index + TopRow + HelpHeaderLine,
+ &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth]
+ );
+ }
+ if (HelpPageIndex == HelpPageCount - 1) {
+ gST->ConOut->SetCursorPosition(gST->ConOut, LocalScreen.RightColumn-1, BottomRow);
+ }
+ }
+ }
+
+ //
+ // Check whether need to print the 'More(D/d)' at the bottom.
+ // Base on current direct info, here shows aligned to the right side of the column.
+ // If the direction is multi line and aligned to right side may have problem, so
+ // add ASSERT code here.
+ //
+ if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) {
+ gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT | FIELD_BACKGROUND);
+ for (Index = 0; Index < HelpBottomLine; Index++) {
+ ASSERT (HelpBottomLine == 1);
+ ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1));
+ PrintStringAt (
+ LocalScreen.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1,
+ Index + BottomRow - HelpBottomLine,
+ &HelpBottomString[Index * BottomLineWidth]
+ );
+ }
+ }
+ //
+ // Reset this flag every time we finish using it.
+ //
+ Repaint = FALSE;
+ NewLine = FALSE;
+ break;
+
+ case CfPrepareToReadKey:
+ ControlFlag = CfReadKey;
+ ScreenOperation = UiNoOperation;
+ break;
+
+ case CfReadKey:
+ ControlFlag = CfScreenOperation;
+
+ //
+ // Wait for user's selection
+ //
+ while (TRUE) {
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // If we encounter error, continue to read another key in.
+ //
+ if (Status != EFI_NOT_READY) {
+ continue;
+ }
+
+ Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
+ ASSERT_EFI_ERROR (Status);
+
+ if (Selection->Action == UI_ACTION_REFRESH_FORMSET) {
+ //
+ // IFR is updated in Callback of refresh opcode, re-parse it
+ //
+ ControlFlag = CfCheckSelection;
+ Selection->Statement = NULL;
+ break;
+ }
+ }
+
+ if (ControlFlag == CfCheckSelection) {
+ break;
+ }
+
+ switch (Key.UnicodeChar) {
+ case CHAR_CARRIAGE_RETURN:
+ if(MenuOption->GrayOut || MenuOption->ReadOnly) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+
+ ScreenOperation = UiSelect;
+ gDirection = 0;
+ break;
+
+ //
+ // We will push the adjustment of these numeric values directly to the input handler
+ // NOTE: we won't handle manual input numeric
+ //
+ case '+':
+ case '-':
+ //
+ // If the screen has no menu items, and the user didn't select UiReset
+ // ignore the selection and go back to reading keys.
+ //
+ if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+
+ ASSERT(MenuOption != NULL);
+ Statement = MenuOption->ThisTag;
+ if ((Statement->Operand == EFI_IFR_DATE_OP)
+ || (Statement->Operand == EFI_IFR_TIME_OP)
+ || ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
+ ){
+ if (Key.UnicodeChar == '+') {
+ gDirection = SCAN_RIGHT;
+ } else {
+ gDirection = SCAN_LEFT;
+ }
+ Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
+ if (EFI_ERROR (Status)) {
+ //
+ // Repaint to clear possible error prompt pop-up
+ //
+ Repaint = TRUE;
+ NewLine = TRUE;
+ } else {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ }
+ if (OptionString != NULL) {
+ FreePool (OptionString);
+ }
+ }
+ break;
+
+ case '^':
+ ScreenOperation = UiUp;
+ break;
+
+ case 'V':
+ case 'v':
+ ScreenOperation = UiDown;
+ break;
+
+ case ' ':
+ if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
+ //
+ // If the screen has no menu items, and the user didn't select UiReset
+ // ignore the selection and go back to reading keys.
+ //
+ if(IsListEmpty (&gMenuOption)) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+
+ ASSERT(MenuOption != NULL);
+ if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
+ ScreenOperation = UiSelect;
+ }
+ }
+ break;
+
+ case 'D':
+ case 'd':
+ if (!MultiHelpPage) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+ ControlFlag = CfUpdateHelpString;
+ HelpPageIndex = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1;
+ break;
+
+ case 'U':
+ case 'u':
+ if (!MultiHelpPage) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+ ControlFlag = CfUpdateHelpString;
+ HelpPageIndex = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0;
+ break;
+
+ case CHAR_NULL:
+ for (Index = 0; Index < mScanCodeNumber; Index++) {
+ if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
+ ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
+ break;
+ }
+ }
+
+ if (Selection->Form->ModalForm && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) {
+ //
+ // ModalForm has no ESC key and Hot Key.
+ //
+ ControlFlag = CfReadKey;
+ } else if (Index == mScanCodeNumber) {
+ //
+ // Check whether Key matches the registered hot key.
+ //
+ HotKey = NULL;
+ if ((gBrowserSettingScope == SystemLevel) ||
+ (Selection->FormEditable && gFunctionKeySetting != NONE_FUNCTION_KEY_SETTING)) {
+ HotKey = GetHotKeyFromRegisterList (&Key);
+ }
+ if (HotKey != NULL) {
+ ScreenOperation = UiHotKey;
+ }
+ }
+ break;
+ }
+ break;
+
+ case CfScreenOperation:
+ if (ScreenOperation != UiReset) {
+ //
+ // If the screen has no menu items, and the user didn't select UiReset
+ // ignore the selection and go back to reading keys.
+ //
+ if (IsListEmpty (&gMenuOption)) {
+ ControlFlag = CfReadKey;
+ break;
+ }
+ }
+
+ for (Index = 0;
+ Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
+ Index++
+ ) {
+ if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
+ ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
+ break;
+ }
+ }
+ break;
+
+ case CfUiSelect:
+ ControlFlag = CfCheckSelection;
+
+ ASSERT(MenuOption != NULL);
+ Statement = MenuOption->ThisTag;
+ if (Statement->Operand == EFI_IFR_TEXT_OP) {
+ break;
+ }
+
+ //
+ // Keep highlight on current MenuOption
+ //
+ Selection->QuestionId = Statement->QuestionId;
+
+ switch (Statement->Operand) {
+ case EFI_IFR_REF_OP:
+ ProcessGotoOpCode(Statement, Selection, &Repaint, &NewLine);
+ break;
+
+ case EFI_IFR_ACTION_OP:
+ //
+ // Process the Config string <ConfigResp>
+ //
+ Status = ProcessQuestionConfig (Selection, Statement);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ //
+ // The action button may change some Question value, so refresh the form
+ //
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ break;
+
+ case EFI_IFR_RESET_BUTTON_OP:
+ //
+ // Reset Question to default value specified by DefaultId
+ //
+ ControlFlag = CfUiDefault;
+ DefaultId = Statement->DefaultId;
+ break;
+
+ default:
+ //
+ // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
+ //
+ UpdateKeyHelp (Selection, MenuOption, TRUE);
+ Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
+
+ if (EFI_ERROR (Status)) {
+ Repaint = TRUE;
+ NewLine = TRUE;
+ UpdateKeyHelp (Selection, MenuOption, FALSE);
+ } else {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ }
+
+ if (OptionString != NULL) {
+ FreePool (OptionString);
+ }
+ break;
+ }
+ break;
+
+ case CfUiReset:
+ //
+ // We come here when someone press ESC
+ //
+ ControlFlag = CfCheckSelection;
+ FindNextMenu (Selection, &Repaint, &NewLine);
+ break;
+
+ case CfUiLeft:
+ ControlFlag = CfCheckSelection;
+ ASSERT(MenuOption != NULL);
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ if (MenuOption->Sequence != 0) {
+ //
+ // In the middle or tail of the Date/Time op-code set, go left.
+ //
+ ASSERT(NewPos != NULL);
+ NewPos = NewPos->BackLink;
+ }
+ }
+ break;
+
+ case CfUiRight:
+ ControlFlag = CfCheckSelection;
+ ASSERT(MenuOption != NULL);
+ if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
+ if (MenuOption->Sequence != 2) {
+ //
+ // In the middle or tail of the Date/Time op-code set, go left.
+ //
+ ASSERT(NewPos != NULL);
+ NewPos = NewPos->ForwardLink;
+ }
+ }
+ break;
+
+ case CfUiUp:
+ ControlFlag = CfCheckSelection;
+
+ SavedListEntry = NewPos;
+
+ ASSERT(NewPos != NULL);
+ //
+ // Adjust Date/Time position before we advance forward.
+ //
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ if (NewPos->BackLink != &gMenuOption) {
+ MenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ ASSERT (MenuOption != NULL);
+ NewLine = TRUE;
+ NewPos = NewPos->BackLink;
+
+ PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ if (PreviousMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, PreviousMenuOption);
+ }
+ DistanceValue = PreviousMenuOption->Skip;
+ Difference = 0;
+ if (MenuOption->Row >= DistanceValue + TopRow) {
+ Difference = MoveToNextStatement (Selection, TRUE, &NewPos, MenuOption->Row - TopRow - DistanceValue);
+ }
+ NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+
+ if (Difference < 0) {
+ //
+ // We hit the begining MenuOption that can be focused
+ // so we simply scroll to the top.
+ //
+ if (TopOfScreen != gMenuOption.ForwardLink) {
+ TopOfScreen = gMenuOption.ForwardLink;
+ Repaint = TRUE;
+ } else {
+ //
+ // Scroll up to the last page when we have arrived at top page.
+ //
+ NewPos = &gMenuOption;
+ TopOfScreen = &gMenuOption;
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+ ScreenOperation = UiPageUp;
+ ControlFlag = CfScreenOperation;
+ break;
+ }
+ } else if (MenuOption->Row < TopRow + DistanceValue + Difference) {
+ //
+ // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
+ //
+ TopOfScreen = NewPos;
+ Repaint = TRUE;
+ SkipValue = 0;
+ } else if (!IsSelectable (NextMenuOption)) {
+ //
+ // Continue to go up until scroll to next page or the selectable option is found.
+ //
+ ScreenOperation = UiUp;
+ ControlFlag = CfScreenOperation;
+ }
+
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+ UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
+ } else {
+ //
+ // Scroll up to the last page.
+ //
+ NewPos = &gMenuOption;
+ TopOfScreen = &gMenuOption;
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+ ScreenOperation = UiPageUp;
+ ControlFlag = CfScreenOperation;
+ }
+ break;
+
+ case CfUiPageUp:
+ //
+ // SkipValue means lines is skipped when show the top menu option.
+ //
+ ControlFlag = CfCheckSelection;
+
+ ASSERT(NewPos != NULL);
+ //
+ // Already at the first menu option, so do nothing.
+ //
+ if (NewPos->BackLink == &gMenuOption) {
+ NewLine = FALSE;
+ Repaint = FALSE;
+ break;
+ }
+
+ NewLine = TRUE;
+ Repaint = TRUE;
+
+ //
+ // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
+ // form of options to be show, so just update the SkipValue to show the next
+ // parts of options.
+ //
+ if (SkipValue > (INTN) (BottomRow - TopRow + 1)) {
+ SkipValue -= BottomRow - TopRow + 1;
+ break;
+ }
+
+ Link = TopOfScreen;
+ //
+ // First minus the menu of the top screen, it's value is SkipValue.
+ //
+ Index = (BottomRow + 1) - SkipValue;
+ while ((Index >= TopRow) && (Link->BackLink != &gMenuOption)) {
+ Link = Link->BackLink;
+ PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
+ if (PreviousMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, PreviousMenuOption);
+ }
+ if (Index < PreviousMenuOption->Skip) {
+ break;
+ }
+ Index = Index - PreviousMenuOption->Skip;
+ }
+
+ if ((Link->BackLink == &gMenuOption) && (Index >= TopRow)) {
+ SkipValue = 0;
+ if (TopOfScreen == &gMenuOption) {
+ TopOfScreen = gMenuOption.ForwardLink;
+ NewPos = gMenuOption.BackLink;
+ MoveToNextStatement (Selection, TRUE, &NewPos, BottomRow - TopRow);
+ Repaint = FALSE;
+ } else if (TopOfScreen != Link) {
+ TopOfScreen = Link;
+ NewPos = Link;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+ } else {
+ //
+ // Finally we know that NewPos is the last MenuOption can be focused.
+ //
+ Repaint = FALSE;
+ NewPos = Link;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+ }
+ } else {
+ if (Index >= TopRow) {
+ //
+ // At here, only case "Index < PreviousMenuOption->Skip" can reach here.
+ //
+ SkipValue = PreviousMenuOption->Skip - (Index - TopRow);
+ } else {
+ SkipValue = PreviousMenuOption->Skip - (TopRow - Index);
+ Link = Link->ForwardLink;
+ }
+
+ //
+ // Move to the option in Next page.
+ //
+ if (TopOfScreen == &gMenuOption) {
+ NewPos = gMenuOption.BackLink;
+ MoveToNextStatement (Selection, TRUE, &NewPos, BottomRow - TopRow);
+ } else {
+ NewPos = Link;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+ }
+
+ //
+ // There are more MenuOption needing scrolling up.
+ //
+ TopOfScreen = Link;
+ MenuOption = NULL;
+ }
+
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ // Don't do this when we are already in the first page.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ break;
+
+ case CfUiPageDown:
+ //
+ // SkipValue means lines is skipped when show the top menu option.
+ //
+ ControlFlag = CfCheckSelection;
+
+ ASSERT (NewPos != NULL);
+ if (NewPos->ForwardLink == &gMenuOption) {
+ NewLine = FALSE;
+ Repaint = FALSE;
+ break;
+ }
+
+ NewLine = TRUE;
+ Repaint = TRUE;
+ Link = TopOfScreen;
+ NextMenuOption = MENU_OPTION_FROM_LINK (Link);
+ Index = TopRow + NextMenuOption->Skip - SkipValue;
+ //
+ // Count to the menu option which will show at the top of the next form.
+ //
+ while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) {
+ Link = Link->ForwardLink;
+ NextMenuOption = MENU_OPTION_FROM_LINK (Link);
+ Index = Index + NextMenuOption->Skip;
+ }
+
+ if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) {
+ //
+ // Finally we know that NewPos is the last MenuOption can be focused.
+ //
+ Repaint = FALSE;
+ MoveToNextStatement (Selection, TRUE, &Link, Index - TopRow);
+ SkipValue = 0;
+ } else {
+ //
+ // Calculate the skip line for top of screen menu.
+ //
+ if (Link == TopOfScreen) {
+ //
+ // The top of screen menu option occupies the entire form.
+ //
+ SkipValue += BottomRow - TopRow + 1;
+ } else {
+ SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1));
+ }
+
+ TopOfScreen = Link;
+ MenuOption = NULL;
+ //
+ // Move to the Next selectable menu.
+ //
+ MoveToNextStatement (Selection, FALSE, &Link, BottomRow - TopRow);
+ }
+
+ //
+ // Save the menu as the next highlight menu.
+ //
+ NewPos = Link;
+
+ //
+ // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
+ // Don't do this when we are already in the last page.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ break;
+
+ case CfUiDown:
+ //
+ // SkipValue means lines is skipped when show the top menu option.
+ // NewPos points to the menu which is highlighted now.
+ //
+ ControlFlag = CfCheckSelection;
+ //
+ // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
+ // to be one that progresses to the next set of op-codes, we need to advance to the last
+ // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
+ // checking can be done. The only other logic we need to introduce is that if a Date/Time
+ // op-code is the last entry in the menu, we need to rewind back to the first op-code of
+ // the Date/Time op-code.
+ //
+ SavedListEntry = NewPos;
+ AdjustDateAndTimePosition (FALSE, &NewPos);
+
+ if (NewPos->ForwardLink != &gMenuOption) {
+ MenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ NewLine = TRUE;
+ NewPos = NewPos->ForwardLink;
+
+ Difference = 0;
+ //
+ // Current menu not at the bottom of the form.
+ //
+ if (BottomRow >= MenuOption->Row + MenuOption->Skip) {
+ //
+ // Find the next selectable menu.
+ //
+ Difference = MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - MenuOption->Row - MenuOption->Skip);
+ //
+ // We hit the end of MenuOption that can be focused
+ // so we simply scroll to the first page.
+ //
+ if (Difference < 0) {
+ //
+ // Scroll to the first page.
+ //
+ if (TopOfScreen != gMenuOption.ForwardLink) {
+ TopOfScreen = gMenuOption.ForwardLink;
+ Repaint = TRUE;
+ MenuOption = NULL;
+ } else {
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+ }
+ NewPos = gMenuOption.ForwardLink;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+
+ SkipValue = 0;
+ //
+ // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ break;
+ }
+ }
+ NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
+ if (NextMenuOption->Row == 0) {
+ UpdateOptionSkipLines (Selection, NextMenuOption);
+ }
+ DistanceValue = Difference + NextMenuOption->Skip;
+
+ Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
+ if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
+ (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
+ NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
+ ) {
+ Temp ++;
+ }
+
+ //
+ // If we are going to scroll, update TopOfScreen
+ //
+ if (Temp > BottomRow) {
+ do {
+ //
+ // Is the current top of screen a zero-advance op-code?
+ // If so, keep moving forward till we hit a >0 advance op-code
+ //
+ SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
+
+ //
+ // If bottom op-code is more than one line or top op-code is more than one line
+ //
+ if ((DistanceValue > 1) || (SavedMenuOption->Skip > 1)) {
+ //
+ // Is the bottom op-code greater than or equal in size to the top op-code?
+ //
+ if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) {
+ //
+ // Skip the top op-code
+ //
+ TopOfScreen = TopOfScreen->ForwardLink;
+ Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue);
+
+ SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
+
+ //
+ // If we have a remainder, skip that many more op-codes until we drain the remainder
+ //
+ while (Difference >= (INTN) SavedMenuOption->Skip) {
+ //
+ // Since the Difference is greater than or equal to this op-code's skip value, skip it
+ //
+ Difference = Difference - (INTN) SavedMenuOption->Skip;
+ TopOfScreen = TopOfScreen->ForwardLink;
+ SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
+ }
+ //
+ // Since we will act on this op-code in the next routine, and increment the
+ // SkipValue, set the skips to one less than what is required.
+ //
+ SkipValue = Difference - 1;
+ } else {
+ //
+ // Since we will act on this op-code in the next routine, and increment the
+ // SkipValue, set the skips to one less than what is required.
+ //
+ SkipValue += (Temp - BottomRow) - 1;
+ }
+ } else {
+ if ((SkipValue + 1) == (INTN) SavedMenuOption->Skip) {
+ TopOfScreen = TopOfScreen->ForwardLink;
+ break;
+ }
+ }
+ //
+ // If the op-code at the top of the screen is more than one line, let's not skip it yet
+ // Let's set a skip flag to smoothly scroll the top of the screen.
+ //
+ if (SavedMenuOption->Skip > 1) {
+ if (SavedMenuOption == NextMenuOption) {
+ SkipValue = 0;
+ } else {
+ SkipValue++;
+ }
+ } else if (SavedMenuOption->Skip == 1) {
+ SkipValue = 0;
+ } else {
+ SkipValue = 0;
+ TopOfScreen = TopOfScreen->ForwardLink;
+ }
+ } while (SavedMenuOption->Skip == 0);
+
+ Repaint = TRUE;
+ } else if (!IsSelectable (NextMenuOption)) {
+ //
+ // Continue to go down until scroll to next page or the selectable option is found.
+ //
+ ScreenOperation = UiDown;
+ ControlFlag = CfScreenOperation;
+ }
+
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+
+ UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
+
+ } else {
+ //
+ // Scroll to the first page.
+ //
+ if (TopOfScreen != gMenuOption.ForwardLink) {
+ TopOfScreen = gMenuOption.ForwardLink;
+ Repaint = TRUE;
+ MenuOption = NULL;
+ } else {
+ //
+ // Need to remove the current highlight menu.
+ // MenuOption saved the last highlight menu info.
+ //
+ MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
+ }
+
+ SkipValue = 0;
+ NewLine = TRUE;
+ //
+ // Get the next highlight menu.
+ //
+ NewPos = gMenuOption.ForwardLink;
+ MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
+ }
+
+ //
+ // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
+ //
+ AdjustDateAndTimePosition (TRUE, &TopOfScreen);
+ AdjustDateAndTimePosition (TRUE, &NewPos);
+ break;
+
+ case CfUiHotKey:
+ ControlFlag = CfCheckSelection;
+
+ Status = EFI_SUCCESS;
+ //
+ // Discard changes. After it, no NV flag is showed.
+ //
+ if ((HotKey->Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
+ Status = DiscardForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
+ if (!EFI_ERROR (Status)) {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ Selection->Statement = NULL;
+ gResetRequired = FALSE;
+ } else {
+ do {
+ CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gDiscardFailed, gPressEnter, gEmptyString);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ //
+ // Still show current page.
+ //
+ Selection->Action = UI_ACTION_NONE;
+ Repaint = TRUE;
+ NewLine = TRUE;
+ break;
+ }
+ }
+
+ //
+ // Reterieve default setting. After it. NV flag will be showed.
+ //
+ if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
+ Status = ExtractDefault (Selection->FormSet, Selection->Form, HotKey->DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE);
+ if (!EFI_ERROR (Status)) {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ Selection->Statement = NULL;
+ gResetRequired = TRUE;
+ } else {
+ do {
+ CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gDefaultFailed, gPressEnter, gEmptyString);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ //
+ // Still show current page.
+ //
+ Selection->Action = UI_ACTION_NONE;
+ Repaint = TRUE;
+ NewLine = TRUE;
+ break;
+ }
+ }
+
+ //
+ // Save changes. After it, no NV flag is showed.
+ //
+ if ((HotKey->Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) {
+ Status = SubmitForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
+ if (!EFI_ERROR (Status)) {
+ ASSERT(MenuOption != NULL);
+ UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
+ UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
+ } else {
+ do {
+ CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gSaveFailed, gPressEnter, gEmptyString);
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ //
+ // Still show current page.
+ //
+ Selection->Action = UI_ACTION_NONE;
+ Repaint = TRUE;
+ NewLine = TRUE;
+ break;
+ }
+ }
+
+ //
+ // Set Reset required Flag
+ //
+ if ((HotKey->Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) {
+ gResetRequired = TRUE;
+ }
+
+ //
+ // Exit Action
+ //
+ if ((HotKey->Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) {
+ //
+ // Form Exit without saving, Similar to ESC Key.
+ // FormSet Exit without saving, Exit SendForm.
+ // System Exit without saving, CallExitHandler and Exit SendForm.
+ //
+ DiscardForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
+ if (gBrowserSettingScope == FormLevel) {
+ ControlFlag = CfUiReset;
+ } else if (gBrowserSettingScope == FormSetLevel) {
+ Selection->Action = UI_ACTION_EXIT;
+ } else if (gBrowserSettingScope == SystemLevel) {
+ if (ExitHandlerFunction != NULL) {
+ ExitHandlerFunction ();
+ }
+ Selection->Action = UI_ACTION_EXIT;
+ }
+ Selection->Statement = NULL;
+ }
+ break;
+
+ case CfUiDefault:
+ ControlFlag = CfCheckSelection;
+ //
+ // Reset to default value for all forms in the whole system.
+ //
+ Status = ExtractDefault (Selection->FormSet, NULL, DefaultId, FormSetLevel, GetDefaultForAll, NULL, FALSE);
+
+ if (!EFI_ERROR (Status)) {
+ Selection->Action = UI_ACTION_REFRESH_FORM;
+ Selection->Statement = NULL;
+ gResetRequired = TRUE;
+ }
+ break;
+
+ case CfUiNoOperation:
+ ControlFlag = CfCheckSelection;
+ break;
+
+ case CfExit:
+ UiFreeRefreshList ();
+
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+ gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);
+ gST->ConOut->OutputString (gST->ConOut, L"\n");
+ if (HelpString != NULL) {
+ FreePool (HelpString);
+ }
+ if (HelpHeaderString != NULL) {
+ FreePool (HelpHeaderString);
+ }
+ if (HelpBottomString != NULL) {
+ FreePool (HelpBottomString);
+ }
+
+ return EFI_SUCCESS;
+
+ default:
+ break;
+ }
+ }
+}