/** @file | |
Utility functions for User Interface functions. | |
Copyright (c) 2004 - 2011, 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 = INITIALIZE_LIST_HEAD_VARIABLE (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 and FormSetGuid in all cached menu list. | |
@param Parent The parent of menu to search. | |
@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_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)) { | |
return Parent; | |
} | |
Link = GetFirstNode (&Parent->ChildListHead); | |
while (!IsNull (&Parent->ChildListHead, Link)) { | |
Child = UI_MENU_LIST_FROM_LINK (Link); | |
MenuList = UiFindChildMenuList (Child, FormSetGuid, FormId); | |
if (MenuList != NULL) { | |
return MenuList; | |
} | |
Link = GetNextNode (&Parent->ChildListHead, Link); | |
} | |
return NULL; | |
} | |
/** | |
Search Menu with given FormSetGuid and FormId in all cached menu list. | |
@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_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, FormSetGuid, FormId); | |
if (Child != NULL) { | |
return Child; | |
} | |
Link = GetNextNode (&gMenuList, Link); | |
} | |
return NULL; | |
} | |
/** | |
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; | |
} | |
} | |
/** | |
Refresh question. | |
@param MenuRefreshEntry Menu refresh structure which has info about the refresh question. | |
**/ | |
EFI_STATUS | |
RefreshQuestion ( | |
IN MENU_REFRESH_ENTRY *MenuRefreshEntry | |
) | |
{ | |
CHAR16 *OptionString; | |
UINTN Index; | |
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, FALSE); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
OptionString = NULL; | |
ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString); | |
if (OptionString != NULL) { | |
// | |
// If leading spaces on OptionString - remove the spaces | |
// | |
for (Index = 0; OptionString[Index] == L' '; Index++) | |
; | |
// | |
// 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); | |
PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, &OptionString[Index]); | |
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 (Statement->GrayOutExpression != NULL) { | |
MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b; | |
} | |
// | |
// 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; | |
} | |
default: | |
MenuOption->IsQuestion = FALSE; | |
break; | |
} | |
if ((Statement->ValueExpression != NULL) || | |
((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) { | |
MenuOption->ReadOnly = 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); | |
VA_START (Marker, KeyValue); | |
// | |
// 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; | |
// | |
// 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); | |
} | |
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. | |
@param InputString String description for this option. | |
@param LineWidth Width of the desired string to extract in CHAR16 | |
characters | |
@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. | |
**/ | |
UINT16 | |
GetLineByWidth ( | |
IN CHAR16 *InputString, | |
IN UINT16 LineWidth, | |
IN OUT UINTN *Index, | |
OUT CHAR16 **OutputString | |
) | |
{ | |
UINT16 Count; | |
UINT16 Count2; | |
if (GetLineByWidthFinished) { | |
GetLineByWidthFinished = FALSE; | |
return (UINT16) 0; | |
} | |
Count = LineWidth; | |
Count2 = 0; | |
*OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2)); | |
// | |
// Ensure we have got a valid buffer | |
// | |
if (*OutputString != NULL) { | |
// | |
//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 (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++) | |
; | |
// | |
// Copy the desired LineWidth of data to the output buffer. | |
// Also make sure that we don't copy more than the string. | |
// Also make sure that if there are linefeeds, we account for them. | |
// | |
if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) && | |
(StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2)) | |
) { | |
// | |
// Convert to CHAR16 value and show that we are done with this operation | |
// | |
LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2); | |
if (LineWidth != 0) { | |
GetLineByWidthFinished = TRUE; | |
} | |
} else { | |
if (Count2 == LineWidth) { | |
// | |
// Rewind the string from the maximum size until we see a space to break the line | |
// | |
for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--) | |
; | |
if (LineWidth == 0) { | |
LineWidth = Count; | |
} | |
} else { | |
LineWidth = Count2; | |
} | |
} | |
CopyMem (*OutputString, &InputString[*Index], LineWidth * 2); | |
// | |
// If currently pointing to a space, increment the index to the first non-space character | |
// | |
for (; | |
(InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN); | |
(*Index)++ | |
) | |
; | |
*Index = (UINT16) (*Index + LineWidth); | |
return LineWidth; | |
} else { | |
return (UINT16) 0; | |
} | |
} | |
/** | |
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; | |
Row = 0; | |
OptionString = NULL; | |
ProcessOptions (Selection, MenuOption, FALSE, &OptionString); | |
if (OptionString != NULL) { | |
Width = (UINT16) gOptionBlockWidth; | |
OriginalRow = Row; | |
for (Index = 0; GetLineByWidth (OptionString, Width, &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); | |
} | |
Row = OriginalRow; | |
} | |
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. | |
**/ | |
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); | |
if (NextMenuOption->Row == 0) { | |
UpdateOptionSkipLines (Selection, NextMenuOption); | |
} | |
if (GoUp && (PreMenuOption != NextMenuOption)) { | |
// | |
// Current Position doesn't need to be caculated when go up. | |
// Caculate distanct at first when go up | |
// | |
if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) { | |
NextMenuOption = PreMenuOption; | |
break; | |
} | |
Distance += NextMenuOption->Skip; | |
} | |
if (IsSelectable (NextMenuOption)) { | |
break; | |
} | |
if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) { | |
// | |
// Arrive at top. | |
// | |
Distance = -1; | |
break; | |
} | |
if (!GoUp) { | |
// | |
// Caculate distanct at later when go down | |
// | |
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; | |
UINTN BufferSize; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
CHAR16 TemStr[2]; | |
UINT8 *DevicePathBuffer; | |
UINTN Index; | |
UINT8 DigitUint8; | |
FORM_BROWSER_FORM *RefForm; | |
EFI_INPUT_KEY Key; | |
EFI_STATUS Status; | |
UI_MENU_LIST *MenuList; | |
BOOLEAN UpdateFormInfo; | |
Status = EFI_SUCCESS; | |
UpdateFormInfo = TRUE; | |
if (Statement->HiiValue.Value.ref.DevicePath != 0) { | |
if (Selection->Form->ModalForm) { | |
return Status; | |
} | |
// | |
// Goto another Hii Package list | |
// | |
Selection->Action = UI_ACTION_REFRESH_FORMSET; | |
StringPtr = GetToken (Statement->HiiValue.Value.ref.DevicePath, Selection->FormSet->HiiHandle); | |
if (StringPtr == NULL) { | |
// | |
// No device path string not found, exit | |
// | |
Selection->Action = UI_ACTION_EXIT; | |
Selection->Statement = NULL; | |
return Status; | |
} | |
BufferSize = StrLen (StringPtr) / 2; | |
DevicePath = AllocatePool (BufferSize); | |
ASSERT (DevicePath != NULL); | |
// | |
// Convert from Device Path String to DevicePath Buffer in the reverse order. | |
// | |
DevicePathBuffer = (UINT8 *) DevicePath; | |
for (Index = 0; StringPtr[Index] != L'\0'; Index ++) { | |
TemStr[0] = StringPtr[Index]; | |
DigitUint8 = (UINT8) StrHexToUint64 (TemStr); | |
if (DigitUint8 == 0 && TemStr[0] != L'0') { | |
// | |
// Invalid Hex Char as the tail. | |
// | |
break; | |
} | |
if ((Index & 1) == 0) { | |
DevicePathBuffer [Index/2] = DigitUint8; | |
} else { | |
DevicePathBuffer [Index/2] = (UINT8) ((DevicePathBuffer [Index/2] << 4) + DigitUint8); | |
} | |
} | |
Selection->Handle = DevicePathToHiiHandle (DevicePath); | |
if (Selection->Handle == NULL) { | |
// | |
// If target Hii Handle not found, exit | |
// | |
Selection->Action = UI_ACTION_EXIT; | |
Selection->Statement = NULL; | |
return Status; | |
} | |
FreePool (StringPtr); | |
FreePool (DevicePath); | |
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)) { | |
Status = EvaluateExpression (Selection->FormSet, RefForm, RefForm->SuppressExpression); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (RefForm->SuppressExpression->Result.Value.b) { | |
// | |
// 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; | |
} | |
} | |
UpdateFormInfo = FALSE; | |
} else { | |
if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { | |
Selection->Action = UI_ACTION_REFRESH_FORM; | |
} | |
UpdateFormInfo = FALSE; | |
} | |
if (UpdateFormInfo) { | |
// | |
// Link current form so that we can always go back when someone hits the ESC | |
// | |
MenuList = UiFindMenuList (&Selection->FormSetGuid, Selection->FormId); | |
if (MenuList == NULL && Selection->CurrentMenu != NULL) { | |
MenuList = UiAddMenuList (Selection->CurrentMenu, Selection->Handle, &Selection->FormSetGuid, Selection->FormId); | |
} | |
} | |
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; | |
INTN OldSkipValue; | |
UINTN DistanceValue; | |
UINTN Row; | |
UINTN Col; | |
UINTN Temp; | |
UINTN Temp2; | |
UINTN TopRow; | |
UINTN BottomRow; | |
UINTN OriginalRow; | |
UINTN Index; | |
UINT32 Count; | |
UINT16 Width; | |
CHAR16 *StringPtr; | |
CHAR16 *OptionString; | |
CHAR16 *OutputString; | |
CHAR16 *FormattedString; | |
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; | |
CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); | |
Status = EFI_SUCCESS; | |
FormattedString = NULL; | |
OptionString = NULL; | |
ScreenOperation = UiNoOperation; | |
NewLine = TRUE; | |
MinRefreshInterval = 0; | |
DefaultId = 0; | |
OutputString = NULL; | |
UpArrow = FALSE; | |
DownArrow = FALSE; | |
SkipValue = 0; | |
OldSkipValue = 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->FormSetGuid, Selection->FormId); | |
if (CurrentMenu == NULL) { | |
// | |
// Current menu not found, add it to the menu tree | |
// | |
CurrentMenu = UiAddMenuList (NULL, 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; | |
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; | |
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; | |
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 | |
); | |
} | |
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) { | |
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; | |
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) { | |
// | |
// If leading spaces on OptionString - remove the spaces | |
// | |
for (Index = 0; OptionString[Index] == L' '; Index++) { | |
MenuOption->OptCol++; | |
} | |
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { | |
OptionString[Count] = OptionString[Index]; | |
Count++; | |
} | |
OptionString[Count] = CHAR_NULL; | |
} | |
Width = (UINT16) gOptionBlockWidth; | |
OriginalRow = Row; | |
for (Index = 0; GetLineByWidth (OptionString, Width, &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); | |
} | |
// | |
// If Question has refresh guid, register the op-code. | |
// | |
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; | |
for (Index = 0; GetLineByWidth (StringPtr, Width, &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); | |
// | |
// Need to handle the bottom of the display | |
// | |
if (MenuOption->Skip > 1) { | |
Row += MenuOption->Skip - SkipValue; | |
SkipValue = 0; | |
} 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 (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 -= OldSkipValue; | |
} | |
Link = Link->ForwardLink; | |
} | |
if (NewPos == Link) { | |
SavedMenuOption = MENU_OPTION_FROM_LINK (Link); | |
} | |
if (Link != NewPos || Index > BottomRow || (Link == NewPos && SavedMenuOption->Row + SavedMenuOption->Skip - 1 > BottomRow)) { | |
// | |
// 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); | |
} | |
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; | |
} | |
SkipValue = Index - BottomRow - 1; | |
if (SkipValue > 0 && SkipValue < (INTN) SavedMenuOption->Skip) { | |
TopOfScreen = Link; | |
OldSkipValue = SkipValue; | |
} 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) | |
) { | |
// | |
// If leading spaces on OptionString - remove the spaces | |
// | |
for (Index = 0; OptionString[Index] == L' '; Index++) | |
; | |
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { | |
OptionString[Count] = OptionString[Index]; | |
Count++; | |
} | |
OptionString[Count] = CHAR_NULL; | |
} | |
Width = (UINT16) gOptionBlockWidth; | |
OriginalRow = MenuOption->Row; | |
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) { | |
if (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) { | |
MenuOption->Row++; | |
} | |
FreePool (OutputString); | |
} | |
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); | |
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) { | |
if (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) { | |
MenuOption->Row++; | |
} | |
FreePool (OutputString); | |
} | |
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) { | |
// | |
// If leading spaces on OptionString - remove the spaces | |
// | |
for (Index = 0; OptionString[Index] == L' '; Index++) | |
; | |
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { | |
OptionString[Count] = OptionString[Index]; | |
Count++; | |
} | |
OptionString[Count] = CHAR_NULL; | |
} | |
Width = (UINT16) gOptionBlockWidth; | |
OriginalRow = MenuOption->Row; | |
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) { | |
if (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) { | |
MenuOption->Row++; | |
} | |
FreePool (OutputString); | |
} | |
MenuOption->Row = OriginalRow; | |
FreePool (OptionString); | |
} else { | |
if (NewLine) { | |
OriginalRow = MenuOption->Row; | |
Width = GetWidth (Statement, MenuOption->Handle); | |
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) { | |
if (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) { | |
MenuOption->Row++; | |
} | |
FreePool (OutputString); | |
} | |
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); | |
} | |
ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow); | |
gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND); | |
for (Index = 0; Index < BottomRow - TopRow; Index++) { | |
// | |
// Pad String with spaces to simulate a clearing of the previous line | |
// | |
for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth * 2]) / 2 < gHelpBlockWidth;) { | |
StrCat (&FormattedString[Index * gHelpBlockWidth * 2], L" "); | |
} | |
PrintStringAt ( | |
LocalScreen.RightColumn - gHelpBlockWidth, | |
Index + TopRow, | |
&FormattedString[Index * gHelpBlockWidth * 2] | |
); | |
} | |
} | |
// | |
// 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 | |
// | |
do { | |
Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval); | |
} while (Status == EFI_TIMEOUT); | |
if (Selection->Action == UI_ACTION_REFRESH_FORMSET) { | |
// | |
// IFR is updated in Callback of refresh opcode, re-parse it | |
// | |
ControlFlag = CfCheckSelection; | |
Selection->Statement = NULL; | |
break; | |
} | |
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); | |
// | |
// If we encounter error, continue to read another key in. | |
// | |
if (EFI_ERROR (Status)) { | |
ControlFlag = CfReadKey; | |
break; | |
} | |
switch (Key.UnicodeChar) { | |
case CHAR_CARRIAGE_RETURN: | |
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)) { | |
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) { | |
ScreenOperation = UiSelect; | |
} | |
} | |
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) || (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; | |
OldSkipValue = 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: | |
ControlFlag = CfCheckSelection; | |
ASSERT(NewPos != NULL); | |
if (NewPos->BackLink == &gMenuOption) { | |
NewLine = FALSE; | |
Repaint = FALSE; | |
break; | |
} | |
NewLine = TRUE; | |
Repaint = TRUE; | |
Link = TopOfScreen; | |
Index = BottomRow; | |
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) { | |
Index = 0; | |
break; | |
} | |
Index = Index - PreviousMenuOption->Skip; | |
} | |
if ((Link->BackLink == &gMenuOption) && (Index >= TopRow)) { | |
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 + 1 < TopRow) { | |
// | |
// Back up the previous option. | |
// | |
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: | |
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; | |
while ((Index <= BottomRow) && (Link->ForwardLink != &gMenuOption)) { | |
Index = Index + NextMenuOption->Skip; | |
Link = Link->ForwardLink; | |
NextMenuOption = MENU_OPTION_FROM_LINK (Link); | |
} | |
if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow)) { | |
// | |
// Finally we know that NewPos is the last MenuOption can be focused. | |
// | |
Repaint = FALSE; | |
MoveToNextStatement (Selection, TRUE, &Link, Index - TopRow); | |
} else { | |
if (Index - 1 > BottomRow) { | |
// | |
// Back up the previous option. | |
// | |
Link = Link->BackLink; | |
} | |
// | |
// There are more MenuOption needing scrolling down. | |
// | |
TopOfScreen = Link; | |
MenuOption = NULL; | |
// | |
// Move to the option in Next page. | |
// | |
MoveToNextStatement (Selection, FALSE, &Link, BottomRow - TopRow); | |
} | |
// | |
// 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. | |
// | |
NewPos = Link; | |
AdjustDateAndTimePosition (TRUE, &TopOfScreen); | |
AdjustDateAndTimePosition (TRUE, &NewPos); | |
break; | |
case CfUiDown: | |
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; | |
if (BottomRow >= MenuOption->Row + MenuOption->Skip) { | |
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); | |
// | |
// 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); | |
// | |
// An option might be multi-line, so we need to reflect that data in the overall skip value | |
// | |
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) || (MenuOption->Skip > 1)) { | |
// | |
// Is the bottom op-code greater than or equal in size to the top op-code? | |
// | |
if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) { | |
// | |
// Skip the top op-code | |
// | |
TopOfScreen = TopOfScreen->ForwardLink; | |
Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue); | |
OldSkipValue = Difference; | |
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 = OldSkipValue + (Temp - BottomRow) - 1; | |
} | |
} else { | |
if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) { | |
TopOfScreen = TopOfScreen->ForwardLink; | |
break; | |
} else { | |
SkipValue = OldSkipValue; | |
} | |
} | |
// | |
// 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; | |
OldSkipValue = SkipValue; | |
} 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 { | |
MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); | |
} | |
NewLine = TRUE; | |
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); | |
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); | |
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"); | |
return EFI_SUCCESS; | |
default: | |
break; | |
} | |
} | |
} |