mirror of https://github.com/acidanthera/audk.git
3667 lines
116 KiB
C
3667 lines
116 KiB
C
/** @file
|
|
Utility functions for User Interface functions.
|
|
|
|
Copyright (c) 2004 - 2012, 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;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Process option string for date/time opcode.
|
|
|
|
@param MenuOption Menu option point to date/time.
|
|
@param OptionString Option string input for process.
|
|
@param AddOptCol Whether need to update MenuOption->OptCol.
|
|
|
|
**/
|
|
VOID
|
|
ProcessStringForDateTime (
|
|
UI_MENU_OPTION *MenuOption,
|
|
CHAR16 *OptionString,
|
|
BOOLEAN AddOptCol
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
FORM_BROWSER_STATEMENT *Statement;
|
|
|
|
ASSERT (MenuOption != NULL && OptionString != NULL);
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++) {
|
|
//
|
|
// Base on the blockspace to get the option column info.
|
|
//
|
|
if (AddOptCol) {
|
|
MenuOption->OptCol++;
|
|
}
|
|
}
|
|
|
|
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Count] = OptionString[Index];
|
|
Count++;
|
|
}
|
|
OptionString[Count] = CHAR_NULL;
|
|
|
|
//
|
|
// Enable to suppress field in the opcode base on the flag.
|
|
//
|
|
if (Statement->Operand == EFI_IFR_DATE_OP) {
|
|
//
|
|
// OptionString format is: <**: **: ****>
|
|
// |month|day|year|
|
|
// 4 3 5
|
|
//
|
|
if ((Statement->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
|
|
//
|
|
// At this point, only "<**:" in the optionstring.
|
|
// Clean the day's ** field, after clean, the format is "< :"
|
|
//
|
|
SetUnicodeMem (&OptionString[1], 2, L' ');
|
|
} else if ((Statement->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
|
|
//
|
|
// At this point, only "**:" in the optionstring.
|
|
// Clean the month's "**" field, after clean, the format is " :"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
} else if ((Statement->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
|
|
//
|
|
// At this point, only "****>" in the optionstring.
|
|
// Clean the year's "****" field, after clean, the format is " >"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 4, L' ');
|
|
}
|
|
} else if (Statement->Operand == EFI_IFR_TIME_OP) {
|
|
//
|
|
// OptionString format is: <**: **: **>
|
|
// |hour|minute|second|
|
|
// 4 3 3
|
|
//
|
|
if ((Statement->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
|
|
//
|
|
// At this point, only "<**:" in the optionstring.
|
|
// Clean the hour's ** field, after clean, the format is "< :"
|
|
//
|
|
SetUnicodeMem (&OptionString[1], 2, L' ');
|
|
} else if ((Statement->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
|
|
//
|
|
// At this point, only "**:" in the optionstring.
|
|
// Clean the minute's "**" field, after clean, the format is " :"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
} else if ((Statement->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
|
|
//
|
|
// At this point, only "**>" in the optionstring.
|
|
// Clean the second's "**" field, after clean, the format is " >"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Refresh question.
|
|
|
|
@param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
|
|
**/
|
|
EFI_STATUS
|
|
RefreshQuestion (
|
|
IN MENU_REFRESH_ENTRY *MenuRefreshEntry
|
|
)
|
|
{
|
|
CHAR16 *OptionString;
|
|
EFI_STATUS Status;
|
|
UI_MENU_SELECTION *Selection;
|
|
FORM_BROWSER_STATEMENT *Question;
|
|
|
|
Selection = MenuRefreshEntry->Selection;
|
|
Question = MenuRefreshEntry->MenuOption->ThisTag;
|
|
|
|
Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
OptionString = NULL;
|
|
ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
|
|
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If old Text is longer than new string, need to clean the old string before paint the newer.
|
|
// This option is no need for time/date opcode, because time/data opcode has fixed string length.
|
|
//
|
|
if ((MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_DATE_OP) &&
|
|
(MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_TIME_OP)) {
|
|
ClearLines (
|
|
MenuRefreshEntry->CurrentColumn,
|
|
MenuRefreshEntry->CurrentColumn + gOptionBlockWidth - 1,
|
|
MenuRefreshEntry->CurrentRow,
|
|
MenuRefreshEntry->CurrentRow,
|
|
PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
|
|
);
|
|
}
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
|
|
ProcessStringForDateTime(MenuRefreshEntry->MenuOption, OptionString, FALSE);
|
|
PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
|
|
FreePool (OptionString);
|
|
}
|
|
|
|
//
|
|
// Question value may be changed, need invoke its Callback()
|
|
//
|
|
Status = ProcessCallBackFunction (Selection, Question, EFI_BROWSER_ACTION_CHANGING, FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Refresh the question which has refresh guid event attribute.
|
|
|
|
@param Event The event which has this function related.
|
|
@param Context The input context info related to this event or the status code return to the caller.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
RefreshQuestionNotify(
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
UI_MENU_SELECTION *Selection;
|
|
|
|
//
|
|
// Reset FormPackage update flag
|
|
//
|
|
mHiiPackageListUpdated = FALSE;
|
|
|
|
MenuRefreshEntry = (MENU_REFRESH_ENTRY *)Context;
|
|
ASSERT (MenuRefreshEntry != NULL);
|
|
Selection = MenuRefreshEntry->Selection;
|
|
|
|
RefreshQuestion (MenuRefreshEntry);
|
|
|
|
if (mHiiPackageListUpdated) {
|
|
//
|
|
// Package list is updated, force to reparse IFR binary of target Formset
|
|
//
|
|
mHiiPackageListUpdated = FALSE;
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Refresh screen.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
RefreshForm (
|
|
VOID
|
|
)
|
|
{
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
EFI_STATUS Status;
|
|
UI_MENU_SELECTION *Selection;
|
|
|
|
if (gMenuRefreshHead != NULL) {
|
|
//
|
|
// call from refresh interval process.
|
|
//
|
|
MenuRefreshEntry = gMenuRefreshHead;
|
|
Selection = MenuRefreshEntry->Selection;
|
|
//
|
|
// Reset FormPackage update flag
|
|
//
|
|
mHiiPackageListUpdated = FALSE;
|
|
|
|
do {
|
|
Status = RefreshQuestion (MenuRefreshEntry);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
MenuRefreshEntry = MenuRefreshEntry->Next;
|
|
|
|
} while (MenuRefreshEntry != NULL);
|
|
|
|
if (mHiiPackageListUpdated) {
|
|
//
|
|
// Package list is updated, force to reparse IFR binary of target Formset
|
|
//
|
|
mHiiPackageListUpdated = FALSE;
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
|
|
/**
|
|
Wait for a given event to fire, or for an optional timeout to expire.
|
|
|
|
@param Event The event to wait for
|
|
@param Timeout An optional timeout value in 100 ns units.
|
|
@param RefreshInterval Menu refresh interval (in seconds).
|
|
|
|
@retval EFI_SUCCESS Event fired before Timeout expired.
|
|
@retval EFI_TIME_OUT Timout expired before Event fired.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UiWaitForSingleEvent (
|
|
IN EFI_EVENT Event,
|
|
IN UINT64 Timeout, OPTIONAL
|
|
IN UINT8 RefreshInterval OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
EFI_EVENT TimerEvent;
|
|
EFI_EVENT WaitList[2];
|
|
|
|
if (Timeout != 0) {
|
|
//
|
|
// Create a timer event
|
|
//
|
|
Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
|
|
//
|
|
// Wait for the original event or the timer
|
|
//
|
|
WaitList[0] = Event;
|
|
WaitList[1] = TimerEvent;
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
gBS->CloseEvent (TimerEvent);
|
|
|
|
//
|
|
// If the timer expired, change the return to timed out
|
|
//
|
|
if (!EFI_ERROR (Status) && Index == 1) {
|
|
Status = EFI_TIMEOUT;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Update screen every second
|
|
//
|
|
if (RefreshInterval == 0) {
|
|
Timeout = ONE_SECOND;
|
|
} else {
|
|
Timeout = RefreshInterval * ONE_SECOND;
|
|
}
|
|
|
|
do {
|
|
Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
|
|
//
|
|
// Wait for the original event or the timer
|
|
//
|
|
WaitList[0] = Event;
|
|
WaitList[1] = TimerEvent;
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
|
|
//
|
|
// If the timer expired, update anything that needs a refresh and keep waiting
|
|
//
|
|
if (!EFI_ERROR (Status) && Index == 1) {
|
|
Status = EFI_TIMEOUT;
|
|
if (RefreshInterval != 0) {
|
|
Status = RefreshForm ();
|
|
}
|
|
}
|
|
|
|
gBS->CloseEvent (TimerEvent);
|
|
} while (Status == EFI_TIMEOUT);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Add one menu option by specified description and context.
|
|
|
|
@param String String description for this option.
|
|
@param Handle Hii handle for the package list.
|
|
@param Form The form this statement belong to.
|
|
@param Statement Statement of this Menu Option.
|
|
@param NumberOfLines Display lines for this Menu Option.
|
|
@param MenuItemCount The index for this Option in the Menu.
|
|
|
|
@retval Pointer Pointer to the added Menu Option.
|
|
|
|
**/
|
|
UI_MENU_OPTION *
|
|
UiAddMenuOption (
|
|
IN CHAR16 *String,
|
|
IN EFI_HII_HANDLE Handle,
|
|
IN FORM_BROWSER_FORM *Form,
|
|
IN FORM_BROWSER_STATEMENT *Statement,
|
|
IN UINT16 NumberOfLines,
|
|
IN UINT16 MenuItemCount
|
|
)
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN Index;
|
|
UINTN Count;
|
|
|
|
Count = 1;
|
|
MenuOption = NULL;
|
|
|
|
if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
|
|
//
|
|
// Add three MenuOptions for Date/Time
|
|
// Data format : [01/02/2004] [11:22:33]
|
|
// Line number : 0 0 1 0 0 1
|
|
//
|
|
NumberOfLines = 0;
|
|
Count = 3;
|
|
|
|
if (Statement->Storage == NULL) {
|
|
//
|
|
// For RTC type of date/time, set default refresh interval to be 1 second
|
|
//
|
|
if (Statement->RefreshInterval == 0) {
|
|
Statement->RefreshInterval = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (MenuOption);
|
|
|
|
MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
|
|
MenuOption->Description = String;
|
|
MenuOption->Handle = Handle;
|
|
MenuOption->ThisTag = Statement;
|
|
MenuOption->EntryNumber = MenuItemCount;
|
|
|
|
if (Index == 2) {
|
|
//
|
|
// Override LineNumber for the MenuOption in Date/Time sequence
|
|
//
|
|
MenuOption->Skip = 1;
|
|
} else {
|
|
MenuOption->Skip = NumberOfLines;
|
|
}
|
|
MenuOption->Sequence = Index;
|
|
|
|
if (EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) == ExpressGrayOut ) {
|
|
MenuOption->GrayOut = TRUE;
|
|
} else {
|
|
MenuOption->GrayOut = FALSE;
|
|
}
|
|
|
|
//
|
|
// If the form or the question has the lock attribute, deal same as grayout.
|
|
//
|
|
if (Form->Locked || Statement->Locked) {
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
|
|
switch (Statement->Operand) {
|
|
case EFI_IFR_ORDERED_LIST_OP:
|
|
case EFI_IFR_ONE_OF_OP:
|
|
case EFI_IFR_NUMERIC_OP:
|
|
case EFI_IFR_TIME_OP:
|
|
case EFI_IFR_DATE_OP:
|
|
case EFI_IFR_CHECKBOX_OP:
|
|
case EFI_IFR_PASSWORD_OP:
|
|
case EFI_IFR_STRING_OP:
|
|
//
|
|
// User could change the value of these items
|
|
//
|
|
MenuOption->IsQuestion = TRUE;
|
|
break;
|
|
|
|
case EFI_IFR_TEXT_OP:
|
|
if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
|
|
//
|
|
// Initializing GrayOut option as TRUE for Text setup options
|
|
// so that those options will be Gray in colour and un selectable.
|
|
//
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
|
|
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);
|
|
|
|
//
|
|
// Zero the outgoing buffer
|
|
//
|
|
ZeroMem (StringBuffer, MaximumStringSize);
|
|
|
|
if (HotKey) {
|
|
if (KeyValue == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
if (StringBuffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
//
|
|
// Disable cursor
|
|
//
|
|
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
|
|
|
|
LargestString = 0;
|
|
|
|
VA_START (Marker, KeyValue);
|
|
|
|
//
|
|
// Determine the largest string in the dialog box
|
|
// Notice we are starting with 1 since String is the first string
|
|
//
|
|
for (Count = 0; Count < NumberOfLines; Count++) {
|
|
StackString = VA_ARG (Marker, CHAR16 *);
|
|
|
|
if (StackString[0] == L' ') {
|
|
InputOffset = Count + 1;
|
|
}
|
|
|
|
if ((GetStringWidth (StackString) / 2) > LargestString) {
|
|
//
|
|
// Size of the string visually and subtract the width by one for the null-terminator
|
|
//
|
|
LargestString = (GetStringWidth (StackString) / 2);
|
|
}
|
|
}
|
|
VA_END (Marker);
|
|
|
|
Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
|
|
Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
|
|
|
|
Count = 0;
|
|
|
|
//
|
|
// Display the Popup
|
|
//
|
|
VA_START (Marker, KeyValue);
|
|
CreateSharedPopUp (LargestString, NumberOfLines, Marker);
|
|
VA_END (Marker);
|
|
|
|
//
|
|
// Take the first key typed and report it back?
|
|
//
|
|
if (HotKey) {
|
|
Status = WaitForKeyStroke (&Key);
|
|
ASSERT_EFI_ERROR (Status);
|
|
CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
|
|
|
|
} else {
|
|
do {
|
|
Status = WaitForKeyStroke (&Key);
|
|
|
|
switch (Key.UnicodeChar) {
|
|
case CHAR_NULL:
|
|
switch (Key.ScanCode) {
|
|
case SCAN_ESC:
|
|
FreePool (TempString);
|
|
FreePool (BufferedString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case CHAR_CARRIAGE_RETURN:
|
|
SelectionComplete = TRUE;
|
|
FreePool (TempString);
|
|
FreePool (BufferedString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
return EFI_SUCCESS;
|
|
break;
|
|
|
|
case CHAR_BACKSPACE:
|
|
if (StringBuffer[0] != CHAR_NULL) {
|
|
for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
|
|
TempString[Index] = StringBuffer[Index];
|
|
}
|
|
//
|
|
// Effectively truncate string by 1 character
|
|
//
|
|
TempString[Index - 1] = CHAR_NULL;
|
|
StrCpy (StringBuffer, TempString);
|
|
}
|
|
|
|
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 StringLen;
|
|
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;
|
|
StringPtr = NULL;
|
|
StringLen = 0;
|
|
|
|
//
|
|
// Prepare the device path check, get the device path info first.
|
|
//
|
|
if (Statement->HiiValue.Value.ref.DevicePath != 0) {
|
|
StringPtr = GetToken (Statement->HiiValue.Value.ref.DevicePath, Selection->FormSet->HiiHandle);
|
|
if (StringPtr != NULL) {
|
|
StringLen = StrLen (StringPtr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether the device path string is a valid string.
|
|
//
|
|
if (Statement->HiiValue.Value.ref.DevicePath != 0 && StringPtr != NULL && StringLen != 0) {
|
|
if (Selection->Form->ModalForm) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Goto another Hii Package list
|
|
//
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
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);
|
|
}
|
|
}
|
|
FreePool (StringPtr);
|
|
|
|
Selection->Handle = DevicePathToHiiHandle (DevicePath);
|
|
FreePool (DevicePath);
|
|
|
|
if (Selection->Handle == NULL) {
|
|
//
|
|
// If target Hii Handle not found, exit
|
|
//
|
|
Selection->Action = UI_ACTION_EXIT;
|
|
Selection->Statement = NULL;
|
|
return Status;
|
|
}
|
|
|
|
CopyMem (&Selection->FormSetGuid,&Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
|
|
Selection->FormId = Statement->HiiValue.Value.ref.FormId;
|
|
Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
|
|
} else if (!CompareGuid (&Statement->HiiValue.Value.ref.FormSetGuid, &gZeroGuid)) {
|
|
if (Selection->Form->ModalForm) {
|
|
return Status;
|
|
}
|
|
//
|
|
// Goto another Formset, check for uncommitted data
|
|
//
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
|
|
Selection->Handle = FormSetGuidToHiiHandle(&Statement->HiiValue.Value.ref.FormSetGuid);
|
|
if (Selection->Handle == NULL) {
|
|
//
|
|
// If target Hii Handle not found, exit
|
|
//
|
|
Selection->Action = UI_ACTION_EXIT;
|
|
Selection->Statement = NULL;
|
|
return Status;
|
|
}
|
|
|
|
CopyMem (&Selection->FormSetGuid, &Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
|
|
Selection->FormId = Statement->HiiValue.Value.ref.FormId;
|
|
Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
|
|
} else if (Statement->HiiValue.Value.ref.FormId != 0) {
|
|
//
|
|
// Check whether target From is suppressed.
|
|
//
|
|
RefForm = IdToForm (Selection->FormSet, Statement->HiiValue.Value.ref.FormId);
|
|
|
|
if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) {
|
|
if (EvaluateExpressionList(RefForm->SuppressExpression, TRUE, Selection->FormSet, RefForm) != ExpressFalse) {
|
|
//
|
|
// Form is suppressed.
|
|
//
|
|
do {
|
|
CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString);
|
|
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
if (Repaint != NULL) {
|
|
*Repaint = TRUE;
|
|
}
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Goto another form inside this formset,
|
|
//
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
|
|
Selection->FormId = Statement->HiiValue.Value.ref.FormId;
|
|
Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
|
|
} else if (Statement->HiiValue.Value.ref.QuestionId != 0) {
|
|
//
|
|
// Goto another Question
|
|
//
|
|
Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
|
|
|
|
if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
} else {
|
|
if (Repaint != NULL) {
|
|
*Repaint = TRUE;
|
|
}
|
|
if (NewLine != NULL) {
|
|
*NewLine = TRUE;
|
|
}
|
|
}
|
|
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;
|
|
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) {
|
|
ProcessStringForDateTime(MenuOption, OptionString, TRUE);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Not find the selected menu in current show page.
|
|
// Have two case to enter this if:
|
|
// 1. Not find the menu at current page.
|
|
// 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
|
|
// For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
|
|
//
|
|
// Base on the selected menu will show at the bottom of the page,
|
|
// select the menu which will show at the top of the page.
|
|
//
|
|
if (Link != NewPos || Index > BottomRow ||
|
|
(Link == NewPos && (SavedMenuOption->Row + SavedMenuOption->Skip - 1 > BottomRow) && (Link != TopOfScreen))) {
|
|
//
|
|
// Find the MenuOption which has the skip value for Date/Time opcode.
|
|
//
|
|
AdjustDateAndTimePosition(FALSE, &NewPos);
|
|
//
|
|
// NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
|
|
//
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
//
|
|
// SavedMenuOption->Row == 0 means the menu not show yet.
|
|
//
|
|
if (SavedMenuOption->Row == 0) {
|
|
UpdateOptionSkipLines (Selection, SavedMenuOption);
|
|
}
|
|
|
|
//
|
|
// Base on the selected menu will show at the bottome of next page,
|
|
// select the menu show at the top of the next page.
|
|
//
|
|
Link = NewPos;
|
|
for (Index = TopRow + SavedMenuOption->Skip; Index <= BottomRow + 1; ) {
|
|
Link = Link->BackLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
if (SavedMenuOption->Row == 0) {
|
|
UpdateOptionSkipLines (Selection, SavedMenuOption);
|
|
}
|
|
Index += SavedMenuOption->Skip;
|
|
}
|
|
|
|
//
|
|
// Found the menu which will show at the top of the page.
|
|
//
|
|
if (Link == NewPos) {
|
|
//
|
|
// The menu can show more than one pages, just show the menu at the top of the page.
|
|
//
|
|
SkipValue = 0;
|
|
TopOfScreen = Link;
|
|
OldSkipValue = SkipValue;
|
|
} else {
|
|
//
|
|
// Check whether need to skip some line for menu shows at the top of the page.
|
|
//
|
|
SkipValue = Index - BottomRow - 1;
|
|
if (SkipValue > 0 && SkipValue < (INTN) SavedMenuOption->Skip) {
|
|
TopOfScreen = Link;
|
|
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)
|
|
) {
|
|
ProcessStringForDateTime(MenuOption, OptionString, FALSE);
|
|
}
|
|
|
|
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) {
|
|
ProcessStringForDateTime(MenuOption, OptionString, FALSE);
|
|
}
|
|
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:
|
|
if(MenuOption->GrayOut || MenuOption->ReadOnly) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
ScreenOperation = UiSelect;
|
|
gDirection = 0;
|
|
break;
|
|
|
|
//
|
|
// We will push the adjustment of these numeric values directly to the input handler
|
|
// NOTE: we won't handle manual input numeric
|
|
//
|
|
case '+':
|
|
case '-':
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiReset
|
|
// ignore the selection and go back to reading keys.
|
|
//
|
|
if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
ASSERT(MenuOption != NULL);
|
|
Statement = MenuOption->ThisTag;
|
|
if ((Statement->Operand == EFI_IFR_DATE_OP)
|
|
|| (Statement->Operand == EFI_IFR_TIME_OP)
|
|
|| ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
|
|
){
|
|
if (Key.UnicodeChar == '+') {
|
|
gDirection = SCAN_RIGHT;
|
|
} else {
|
|
gDirection = SCAN_LEFT;
|
|
}
|
|
Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Repaint to clear possible error prompt pop-up
|
|
//
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
} else {
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
}
|
|
if (OptionString != NULL) {
|
|
FreePool (OptionString);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '^':
|
|
ScreenOperation = UiUp;
|
|
break;
|
|
|
|
case 'V':
|
|
case 'v':
|
|
ScreenOperation = UiDown;
|
|
break;
|
|
|
|
case ' ':
|
|
if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiReset
|
|
// ignore the selection and go back to reading keys.
|
|
//
|
|
if(IsListEmpty (&gMenuOption)) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
ASSERT(MenuOption != NULL);
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
|
|
ScreenOperation = UiSelect;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 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;
|
|
}
|
|
}
|
|
}
|