| /** @file |
| |
| Copyright (c) 2004 - 2007, Intel Corporation |
| All rights reserved. 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. |
| |
| Module Name: |
| |
| Ui.c |
| |
| Abstract: |
| |
| Implementation for UI. |
| |
| Revision History |
| |
| |
| **/ |
| |
| #include "Ui.h" |
| #include "Setup.h" |
| |
| LIST_ENTRY Menu; |
| LIST_ENTRY gMenuList; |
| MENU_REFRESH_ENTRY *gMenuRefreshHead; |
| |
| // |
| // 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_F2, |
| UiPrevious, |
| SCAN_LEFT, |
| UiLeft, |
| SCAN_RIGHT, |
| UiRight, |
| SCAN_F9, |
| UiDefault, |
| SCAN_F10, |
| UiSave |
| }; |
| |
| SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = { |
| UiNoOperation, |
| CfUiNoOperation, |
| UiDefault, |
| CfUiDefault, |
| UiSelect, |
| CfUiSelect, |
| UiUp, |
| CfUiUp, |
| UiDown, |
| CfUiDown, |
| UiLeft, |
| CfUiLeft, |
| UiRight, |
| CfUiRight, |
| UiReset, |
| CfUiReset, |
| UiSave, |
| CfUiSave, |
| UiPrevious, |
| CfUiPrevious, |
| UiPageUp, |
| CfUiPageUp, |
| UiPageDown, |
| CfUiPageDown |
| }; |
| |
| |
| /** |
| 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. |
| |
| @return None |
| |
| **/ |
| VOID |
| SetUnicodeMem ( |
| IN VOID *Buffer, |
| IN UINTN Size, |
| IN CHAR16 Value |
| ) |
| { |
| CHAR16 *Ptr; |
| |
| Ptr = Buffer; |
| while (Size--) { |
| *(Ptr++) = Value; |
| } |
| } |
| |
| |
| /** |
| Initialize Menu option list. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiInitMenu ( |
| VOID |
| ) |
| { |
| InitializeListHead (&Menu); |
| } |
| |
| |
| /** |
| Initialize Menu option list. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiInitMenuList ( |
| VOID |
| ) |
| { |
| InitializeListHead (&gMenuList); |
| } |
| |
| |
| /** |
| Remove a Menu in list, and return FormId/QuestionId for previous Menu. |
| |
| @param Selection Menu selection. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiRemoveMenuListEntry ( |
| IN OUT UI_MENU_SELECTION *Selection |
| ) |
| { |
| UI_MENU_LIST *UiMenuList; |
| |
| if (!IsListEmpty (&gMenuList)) { |
| UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE); |
| |
| Selection->FormId = UiMenuList->FormId; |
| Selection->QuestionId = UiMenuList->QuestionId; |
| RemoveEntryList (&UiMenuList->MenuLink); |
| gBS->FreePool (UiMenuList); |
| } |
| } |
| |
| |
| /** |
| Free Menu option linked list. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiFreeMenuList ( |
| VOID |
| ) |
| { |
| UI_MENU_LIST *UiMenuList; |
| |
| while (!IsListEmpty (&gMenuList)) { |
| UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE); |
| RemoveEntryList (&UiMenuList->MenuLink); |
| gBS->FreePool (UiMenuList); |
| } |
| } |
| |
| |
| /** |
| Add one menu entry to the linked lst |
| |
| @param Selection Menu selection. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiAddMenuListEntry ( |
| IN UI_MENU_SELECTION *Selection |
| ) |
| { |
| UI_MENU_LIST *UiMenuList; |
| |
| UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST)); |
| ASSERT (UiMenuList != NULL); |
| |
| UiMenuList->Signature = UI_MENU_LIST_SIGNATURE; |
| UiMenuList->FormId = Selection->FormId; |
| UiMenuList->QuestionId = Selection->QuestionId; |
| |
| InsertHeadList (&gMenuList, &UiMenuList->MenuLink); |
| } |
| |
| |
| /** |
| Free Menu option linked list. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiFreeMenu ( |
| VOID |
| ) |
| { |
| UI_MENU_OPTION *MenuOption; |
| |
| while (!IsListEmpty (&Menu)) { |
| MenuOption = MENU_OPTION_FROM_LINK (Menu.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 |
| // |
| gBS->FreePool (MenuOption->Description); |
| } |
| gBS->FreePool (MenuOption); |
| } |
| } |
| |
| |
| /** |
| Free Menu option linked list. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiFreeRefreshList ( |
| VOID |
| ) |
| { |
| MENU_REFRESH_ENTRY *OldMenuRefreshEntry; |
| |
| while (gMenuRefreshHead != NULL) { |
| OldMenuRefreshEntry = gMenuRefreshHead->Next; |
| gBS->FreePool (gMenuRefreshHead); |
| gMenuRefreshHead = OldMenuRefreshEntry; |
| } |
| |
| gMenuRefreshHead = NULL; |
| } |
| |
| |
| |
| /** |
| Refresh screen. |
| |
| None. |
| |
| @return None. |
| |
| **/ |
| VOID |
| RefreshForm ( |
| VOID |
| ) |
| { |
| CHAR16 *OptionString; |
| MENU_REFRESH_ENTRY *MenuRefreshEntry; |
| UINTN Index; |
| UINTN Loop; |
| EFI_STATUS Status; |
| UI_MENU_SELECTION *Selection; |
| FORM_BROWSER_STATEMENT *Question; |
| |
| OptionString = NULL; |
| |
| if (gMenuRefreshHead != NULL) { |
| |
| MenuRefreshEntry = gMenuRefreshHead; |
| |
| do { |
| gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute); |
| |
| Selection = MenuRefreshEntry->Selection; |
| Question = MenuRefreshEntry->MenuOption->ThisTag; |
| |
| // |
| // Don't update Question being edited |
| // |
| if (Question != MenuRefreshEntry->Selection->Statement) { |
| |
| Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE); |
| if (EFI_ERROR (Status)) { |
| return; |
| } |
| |
| ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString); |
| |
| if (OptionString != NULL) { |
| // |
| // If leading spaces on OptionString - remove the spaces |
| // |
| for (Index = 0; OptionString[Index] == L' '; Index++) |
| ; |
| |
| for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) { |
| OptionString[Loop] = OptionString[Index]; |
| Loop++; |
| } |
| |
| OptionString[Loop] = CHAR_NULL; |
| |
| PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString); |
| gBS->FreePool (OptionString); |
| } |
| } |
| |
| MenuRefreshEntry = MenuRefreshEntry->Next; |
| |
| } while (MenuRefreshEntry != NULL); |
| } |
| } |
| |
| |
| /** |
| 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) { |
| // |
| // 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) { |
| 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 Statement Statement of this Menu Option. |
| @param NumberOfLines Display lines for this Menu Option. |
| @param MenuItemCount The index for this Option in the Menu. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UiAddMenuOption ( |
| IN CHAR16 *String, |
| IN EFI_HII_HANDLE Handle, |
| IN FORM_BROWSER_STATEMENT *Statement, |
| IN UINT16 NumberOfLines, |
| IN UINT16 MenuItemCount |
| ) |
| { |
| UI_MENU_OPTION *MenuOption; |
| UINTN Index; |
| UINTN Count; |
| |
| Count = 1; |
| |
| if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) { |
| // |
| // Add three MenuOptions for Date/Time |
| // Data format : [01/02/2004] [11:22:33] |
| // Line number : 0 0 1 0 0 1 |
| // |
| NumberOfLines = 0; |
| Count = 3; |
| |
| if (Statement->Storage == NULL) { |
| // |
| // For RTC type of date/time, set default refresh interval to be 1 second |
| // |
| if (Statement->RefreshInterval == 0) { |
| Statement->RefreshInterval = 1; |
| } |
| } |
| } |
| |
| for (Index = 0; Index < Count; Index++) { |
| MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION)); |
| ASSERT (MenuOption); |
| |
| MenuOption->Signature = UI_MENU_OPTION_SIGNATURE; |
| MenuOption->Description = String; |
| MenuOption->Handle = Handle; |
| MenuOption->ThisTag = Statement; |
| MenuOption->EntryNumber = MenuItemCount; |
| |
| if (Index == 2) { |
| // |
| // Override LineNumber for the MenuOption in Date/Time sequence |
| // |
| MenuOption->Skip = 1; |
| } else { |
| MenuOption->Skip = NumberOfLines; |
| } |
| MenuOption->Sequence = Index; |
| |
| if (Statement->GrayOutExpression != NULL) { |
| MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b; |
| } |
| |
| if ((Statement->ValueExpression != NULL) || |
| (Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY)) { |
| MenuOption->ReadOnly = TRUE; |
| } |
| |
| InsertTailList (&Menu, &MenuOption->Link); |
| } |
| } |
| |
| |
| /** |
| 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 String Pointer to the first string in the list |
| @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 |
| CreateDialog ( |
| IN UINTN NumberOfLines, |
| IN BOOLEAN HotKey, |
| IN UINTN MaximumStringSize, |
| OUT CHAR16 *StringBuffer, |
| OUT EFI_INPUT_KEY *KeyValue, |
| IN CHAR16 *String, |
| ... |
| ) |
| { |
| VA_LIST Marker; |
| UINTN Count; |
| EFI_INPUT_KEY Key; |
| UINTN LargestString; |
| CHAR16 *TempString; |
| CHAR16 *BufferedString; |
| CHAR16 *StackString; |
| CHAR16 KeyPad[2]; |
| UINTN Start; |
| UINTN Top; |
| UINTN Index; |
| EFI_STATUS Status; |
| BOOLEAN SelectionComplete; |
| UINTN InputOffset; |
| UINTN CurrentAttribute; |
| UINTN DimensionsWidth; |
| UINTN DimensionsHeight; |
| |
| DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn; |
| DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow; |
| |
| SelectionComplete = FALSE; |
| InputOffset = 0; |
| TempString = AllocateZeroPool (MaximumStringSize * 2); |
| BufferedString = AllocateZeroPool (MaximumStringSize * 2); |
| CurrentAttribute = gST->ConOut->Mode->Attribute; |
| |
| ASSERT (TempString); |
| ASSERT (BufferedString); |
| |
| VA_START (Marker, String); |
| |
| // |
| // 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 = (GetStringWidth (String) / 2); |
| |
| if (*String == L' ') { |
| InputOffset = 1; |
| } |
| // |
| // Determine the largest string in the dialog box |
| // Notice we are starting with 1 since String is the first string |
| // |
| for (Count = 1; 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); |
| } |
| } |
| |
| Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1; |
| Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1; |
| |
| Count = 0; |
| |
| // |
| // Display the Popup |
| // |
| CreateSharedPopUp (LargestString, NumberOfLines, &String); |
| |
| // |
| // 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: |
| gBS->FreePool (TempString); |
| gBS->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; |
| gBS->FreePool (TempString); |
| gBS->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; |
| } |
| |
| VOID |
| CreateSharedPopUp ( |
| IN UINTN RequestedWidth, |
| IN UINTN NumberOfLines, |
| IN CHAR16 **ArrayOfStrings |
| ) |
| { |
| 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; |
| |
| Count = 0; |
| |
| 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; |
| for (Index = Top; Index + 2 < Bottom; Index++) { |
| String = ArrayOfStrings[Count]; |
| Count++; |
| |
| // |
| // 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); |
| } |
| |
| VOID |
| CreatePopUp ( |
| IN UINTN RequestedWidth, |
| IN UINTN NumberOfLines, |
| IN CHAR16 *ArrayOfStrings, |
| ... |
| ) |
| { |
| CreateSharedPopUp (RequestedWidth, NumberOfLines, &ArrayOfStrings); |
| } |
| |
| |
| /** |
| Update status bar on the bottom of menu. |
| |
| @param MessageType The type of message to be shown. |
| @param Flags The flags in Question header. |
| @param State Set or clear. |
| |
| @return None. |
| |
| **/ |
| VOID |
| UpdateStatusBar ( |
| IN UINTN MessageType, |
| IN UINT8 Flags, |
| IN BOOLEAN State |
| ) |
| { |
| UINTN Index; |
| STATIC BOOLEAN InputError; |
| CHAR16 *NvUpdateMessage; |
| CHAR16 *InputErrorMessage; |
| |
| 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 |
| ); |
| InputError = TRUE; |
| } else { |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT); |
| for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) { |
| PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" "); |
| } |
| |
| InputError = FALSE; |
| } |
| break; |
| |
| case NV_UPDATE_REQUIRED: |
| if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) { |
| if (State) { |
| gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT); |
| PrintStringAt ( |
| gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth, |
| gScreenDimensions.BottomRow - 1, |
| NvUpdateMessage |
| ); |
| gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED)); |
| |
| gNvUpdateRequired = TRUE; |
| } else { |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT); |
| for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) { |
| PrintAt ( |
| (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index), |
| gScreenDimensions.BottomRow - 1, |
| L" " |
| ); |
| } |
| |
| gNvUpdateRequired = FALSE; |
| } |
| } |
| break; |
| |
| case REFRESH_STATUS_BAR: |
| if (InputError) { |
| UpdateStatusBar (INPUT_ERROR, Flags, TRUE); |
| } |
| |
| if (gNvUpdateRequired) { |
| UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| gBS->FreePool (InputErrorMessage); |
| gBS->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); |
| gBS->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 Width; |
| } |
| |
| |
| /** |
| 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 |
| ) |
| { |
| static BOOLEAN Finished; |
| UINT16 Count; |
| UINT16 Count2; |
| |
| if (Finished) { |
| Finished = 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) { |
| Finished = 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 MenuOption The MenuOption to be checked. |
| |
| @retval TRUE This Menu Option is selectable. |
| @retval FALSE This Menu Option could not be selected. |
| |
| **/ |
| VOID |
| UpdateOptionSkipLines ( |
| IN UI_MENU_SELECTION *Selection, |
| IN UI_MENU_OPTION *MenuOption, |
| IN CHAR16 **OptionalString, |
| IN UINTN SkipValue |
| ) |
| { |
| UINTN Index; |
| UINT16 Width; |
| UINTN Row; |
| UINTN OriginalRow; |
| CHAR16 *OutputString; |
| CHAR16 *OptionString; |
| |
| Row = 0; |
| OptionString = *OptionalString; |
| OutputString = 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])) { |
| if (SkipValue == 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++; |
| } |
| } |
| } |
| |
| gBS->FreePool (OutputString); |
| if (SkipValue != 0) { |
| SkipValue--; |
| } |
| } |
| |
| Row = OriginalRow; |
| } |
| |
| *OptionalString = OptionString; |
| } |
| |
| |
| /** |
| Check whether this Menu Option could be highlighted. |
| |
| @param MenuOption The MenuOption to be checked. |
| |
| @retval TRUE This Menu Option is selectable. |
| @retval FALSE This Menu Option could not be selected. |
| |
| **/ |
| STATIC |
| 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. |
| |
| @param Direction the scroll direction. False is down. True is up. |
| |
| @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. |
| |
| **/ |
| STATIC |
| BOOLEAN |
| ValueIsScroll ( |
| IN BOOLEAN Direction, |
| IN LIST_ENTRY *CurrentPos |
| ) |
| { |
| LIST_ENTRY *Temp; |
| UI_MENU_OPTION *MenuOption; |
| |
| Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink; |
| |
| if (Temp == &Menu) { |
| return TRUE; |
| } |
| |
| for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) { |
| MenuOption = MENU_OPTION_FROM_LINK (Temp); |
| if (IsSelectable (MenuOption)) { |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| Move to next selectable statement. |
| |
| @param GoUp The navigation direction. TRUE: up, FALSE: down. |
| @param CurrentPosition Current position. |
| |
| @return The row distance from current MenuOption to next selectable MenuOption. |
| |
| **/ |
| STATIC |
| INTN |
| MoveToNextStatement ( |
| IN BOOLEAN GoUp, |
| IN OUT LIST_ENTRY **CurrentPosition |
| ) |
| { |
| INTN Distance; |
| LIST_ENTRY *Pos; |
| BOOLEAN HitEnd; |
| UI_MENU_OPTION *NextMenuOption; |
| |
| Distance = 0; |
| Pos = *CurrentPosition; |
| HitEnd = FALSE; |
| |
| while (TRUE) { |
| NextMenuOption = MENU_OPTION_FROM_LINK (Pos); |
| if (IsSelectable (NextMenuOption)) { |
| break; |
| } |
| if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) { |
| HitEnd = TRUE; |
| break; |
| } |
| Distance += NextMenuOption->Skip; |
| Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink); |
| } |
| |
| if (HitEnd) { |
| // |
| // If we hit end there is still no statement can be focused, |
| // we go backwards to find the statement can be focused. |
| // |
| Distance = 0; |
| Pos = *CurrentPosition; |
| |
| while (TRUE) { |
| NextMenuOption = MENU_OPTION_FROM_LINK (Pos); |
| if (IsSelectable (NextMenuOption)) { |
| break; |
| } |
| if ((!GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) { |
| ASSERT (FALSE); |
| break; |
| } |
| Distance -= NextMenuOption->Skip; |
| 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 |
| |
| @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. |
| |
| **/ |
| STATIC |
| 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; |
| } |
| |
| |
| /** |
| 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. |
| |
| |
| @return Return the pointer of the menu which selected, |
| @return otherwise return NULL. |
| |
| **/ |
| EFI_STATUS |
| UiDisplayMenu ( |
| IN OUT UI_MENU_SELECTION *Selection |
| ) |
| { |
| INTN SkipValue; |
| INTN Difference; |
| INTN OldSkipValue; |
| UINTN DistanceValue; |
| UINTN Row; |
| UINTN Col; |
| UINTN Temp; |
| UINTN Temp2; |
| UINTN TopRow; |
| UINTN BottomRow; |
| UINTN OriginalRow; |
| UINTN Index; |
| UINT32 Count; |
| UINT16 Width; |
| CHAR16 *StringPtr; |
| CHAR16 *OptionString; |
| CHAR16 *OutputString; |
| CHAR16 *FormattedString; |
| CHAR16 YesResponse; |
| CHAR16 NoResponse; |
| BOOLEAN NewLine; |
| BOOLEAN Repaint; |
| BOOLEAN SavedValue; |
| 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; |
| UI_SCREEN_OPERATION ScreenOperation; |
| UINT8 MinRefreshInterval; |
| UINTN BufferSize; |
| UINT16 DefaultId; |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; |
| FORM_BROWSER_STATEMENT *Statement; |
| |
| CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); |
| |
| Status = EFI_SUCCESS; |
| FormattedString = NULL; |
| OptionString = NULL; |
| ScreenOperation = UiNoOperation; |
| NewLine = TRUE; |
| MinRefreshInterval = 0; |
| DefaultId = 0; |
| |
| OutputString = NULL; |
| gUpArrow = FALSE; |
| gDownArrow = FALSE; |
| SkipValue = 0; |
| OldSkipValue = 0; |
| MenuRefreshEntry = gMenuRefreshHead; |
| |
| NextMenuOption = NULL; |
| PreviousMenuOption = NULL; |
| SavedMenuOption = NULL; |
| |
| ZeroMem (&Key, sizeof (EFI_INPUT_KEY)); |
| |
| if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) { |
| 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; |
| } |
| |
| Col = LocalScreen.LeftColumn; |
| BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1; |
| |
| Selection->TopRow = TopRow; |
| Selection->BottomRow = BottomRow; |
| Selection->PromptCol = Col; |
| Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn; |
| Selection->Statement = NULL; |
| |
| TopOfScreen = Menu.ForwardLink; |
| Repaint = TRUE; |
| MenuOption = NULL; |
| |
| // |
| // Get user's selection |
| // |
| NewPos = Menu.ForwardLink; |
| |
| gST->ConOut->EnableCursor (gST->ConOut, FALSE); |
| UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE); |
| |
| ControlFlag = CfInitialization; |
| Selection->Action = UI_ACTION_NONE; |
| while (TRUE) { |
| switch (ControlFlag) { |
| case CfInitialization: |
| if (IsListEmpty (&Menu)) { |
| 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 |
| // |
| gDownArrow = FALSE; |
| gUpArrow = FALSE; |
| Row = TopRow; |
| |
| Temp = SkipValue; |
| Temp2 = SkipValue; |
| |
| ClearLines ( |
| LocalScreen.LeftColumn, |
| LocalScreen.RightColumn, |
| TopRow - SCROLL_ARROW_HEIGHT, |
| BottomRow + SCROLL_ARROW_HEIGHT, |
| FIELD_TEXT | FIELD_BACKGROUND |
| ); |
| |
| UiFreeRefreshList (); |
| MinRefreshInterval = 0; |
| |
| for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) { |
| MenuOption = MENU_OPTION_FROM_LINK (Link); |
| MenuOption->Row = Row; |
| MenuOption->Col = Col; |
| 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, SUBTITLE_TEXT | FIELD_BACKGROUND); |
| } |
| } |
| |
| Width = GetWidth (Statement, MenuOption->Handle); |
| OriginalRow = Row; |
| |
| 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])) { |
| if (Temp == 0) { |
| Row++; |
| } |
| } |
| |
| gBS->FreePool (OutputString); |
| if (Temp != 0) { |
| Temp--; |
| } |
| } |
| |
| Temp = 0; |
| Row = OriginalRow; |
| |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND); |
| ProcessOptions (Selection, MenuOption, FALSE, &OptionString); |
| |
| if (OptionString != NULL) { |
| if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) { |
| // |
| // If leading spaces on OptionString - remove the spaces |
| // |
| for (Index = 0; OptionString[Index] == L' '; Index++) { |
| MenuOption->OptCol++; |
| } |
| |
| for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { |
| OptionString[Count] = OptionString[Index]; |
| Count++; |
| } |
| |
| OptionString[Count] = CHAR_NULL; |
| } |
| |
| // |
| // 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)); |
| ASSERT (MenuRefreshEntry != NULL); |
| MenuRefreshEntry->MenuOption = MenuOption; |
| MenuRefreshEntry->Selection = Selection; |
| MenuRefreshEntry->CurrentColumn = MenuOption->OptCol; |
| MenuRefreshEntry->CurrentRow = MenuOption->Row; |
| MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND; |
| gMenuRefreshHead = MenuRefreshEntry; |
| } else { |
| // |
| // Advance to the last entry |
| // |
| for (MenuRefreshEntry = gMenuRefreshHead; |
| MenuRefreshEntry->Next != NULL; |
| MenuRefreshEntry = MenuRefreshEntry->Next |
| ) |
| ; |
| MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY)); |
| ASSERT (MenuRefreshEntry->Next != NULL); |
| MenuRefreshEntry = MenuRefreshEntry->Next; |
| MenuRefreshEntry->MenuOption = MenuOption; |
| MenuRefreshEntry->Selection = Selection; |
| MenuRefreshEntry->CurrentColumn = MenuOption->OptCol; |
| MenuRefreshEntry->CurrentRow = MenuOption->Row; |
| MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND; |
| } |
| } |
| |
| 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])) { |
| 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++; |
| } |
| } |
| } |
| |
| gBS->FreePool (OutputString); |
| if (Temp2 != 0) { |
| Temp2--; |
| } |
| } |
| |
| Temp2 = 0; |
| Row = OriginalRow; |
| |
| gBS->FreePool (OptionString); |
| } |
| // |
| // 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])) { |
| 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++; |
| } |
| } |
| } |
| |
| gBS->FreePool (OutputString); |
| if (Temp2 != 0) { |
| Temp2--; |
| } |
| } |
| |
| Row = OriginalRow; |
| gBS->FreePool (StringPtr); |
| } |
| |
| // |
| // 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)) { |
| gDownArrow = TRUE; |
| } |
| |
| Row = BottomRow + 1; |
| break; |
| } |
| } |
| |
| if (!ValueIsScroll (TRUE, TopOfScreen)) { |
| gUpArrow = TRUE; |
| } |
| |
| if (gUpArrow) { |
| 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, FIELD_TEXT | FIELD_BACKGROUND); |
| } |
| |
| if (gDownArrow) { |
| 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, FIELD_TEXT | 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; |
| |
| // |
| // 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 = Menu.ForwardLink; |
| SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| |
| while (SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId && NewPos->ForwardLink != &Menu) { |
| 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; |
| Link = Link->ForwardLink; |
| } |
| |
| if (Link != NewPos || Index > BottomRow) { |
| // |
| // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page |
| // |
| Link = NewPos; |
| for (Index = TopRow; Index <= BottomRow; ) { |
| Link = Link->BackLink; |
| SavedMenuOption = MENU_OPTION_FROM_LINK (Link); |
| Index += SavedMenuOption->Skip; |
| } |
| 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, FIELD_TEXT | FIELD_BACKGROUND); |
| if (OptionString != NULL) { |
| if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || |
| (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP) |
| ) { |
| // |
| // If leading spaces on OptionString - remove the spaces |
| // |
| for (Index = 0; OptionString[Index] == L' '; Index++) |
| ; |
| |
| for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { |
| OptionString[Count] = OptionString[Index]; |
| Count++; |
| } |
| |
| OptionString[Count] = CHAR_NULL; |
| } |
| |
| Width = (UINT16) gOptionBlockWidth; |
| OriginalRow = MenuOption->Row; |
| |
| for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) { |
| if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) { |
| PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString); |
| } |
| // |
| // If there is more string to process print on the next row and increment the Skip value |
| // |
| if (StrLen (&OptionString[Index])) { |
| MenuOption->Row++; |
| } |
| |
| gBS->FreePool (OutputString); |
| } |
| |
| MenuOption->Row = OriginalRow; |
| |
| gBS->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, SUBTITLE_TEXT | 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])) { |
| MenuOption->Row++; |
| } |
| |
| gBS->FreePool (OutputString); |
| } |
| |
| MenuOption->Row = OriginalRow; |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND); |
| } |
| } |
| } |
| |
| // |
| // This is only possible if we entered this page and the first menu option is |
| // a "non-menu" item. In that case, force it UiDown |
| // |
| MenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| if (!IsSelectable (MenuOption)) { |
| ASSERT (ScreenOperation == UiNoOperation); |
| ScreenOperation = UiDown; |
| ControlFlag = CfScreenOperation; |
| break; |
| } |
| |
| // |
| // This is the current selected statement |
| // |
| Statement = MenuOption->ThisTag; |
| Selection->Statement = Statement; |
| |
| // |
| // Set reverse attribute |
| // |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT); |
| 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) { |
| MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND; |
| if (MenuRefreshEntry->MenuOption == MenuOption) { |
| MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT; |
| } |
| } |
| } |
| |
| ProcessOptions (Selection, MenuOption, FALSE, &OptionString); |
| if (OptionString != NULL) { |
| if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) { |
| // |
| // If leading spaces on OptionString - remove the spaces |
| // |
| for (Index = 0; OptionString[Index] == L' '; Index++) |
| ; |
| |
| for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) { |
| OptionString[Count] = OptionString[Index]; |
| Count++; |
| } |
| |
| OptionString[Count] = CHAR_NULL; |
| } |
| Width = (UINT16) gOptionBlockWidth; |
| |
| OriginalRow = MenuOption->Row; |
| |
| for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) { |
| if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) { |
| PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString); |
| } |
| // |
| // If there is more string to process print on the next row and increment the Skip value |
| // |
| if (StrLen (&OptionString[Index])) { |
| MenuOption->Row++; |
| } |
| |
| gBS->FreePool (OutputString); |
| } |
| |
| MenuOption->Row = OriginalRow; |
| |
| gBS->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])) { |
| MenuOption->Row++; |
| } |
| |
| gBS->FreePool (OutputString); |
| } |
| |
| MenuOption->Row = OriginalRow; |
| |
| } |
| } |
| |
| if (((NewPos->ForwardLink != &Menu) && (ScreenOperation == UiDown)) || |
| ((NewPos->BackLink != &Menu) && (ScreenOperation == UiUp)) || |
| (ScreenOperation == UiNoOperation) |
| ) { |
| UpdateKeyHelp (MenuOption, FALSE); |
| } |
| // |
| // Clear reverse attribute |
| // |
| gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | 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 ((Repaint || NewLine) && (gClassOfVfr != EFI_GENERAL_APPLICATION_SUBCLASS)) { |
| // |
| // Don't print anything if it is a NULL help token |
| // |
| if (MenuOption->ThisTag->Help == 0) { |
| 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 (Status == EFI_TIMEOUT) { |
| Key.UnicodeChar = CHAR_CARRIAGE_RETURN; |
| } else { |
| Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); |
| // |
| // if we encounter error, continue to read another key in. |
| // |
| if (EFI_ERROR (Status)) { |
| ControlFlag = CfReadKey; |
| continue; |
| } |
| } |
| |
| if (IsListEmpty (&Menu) && Key.UnicodeChar != CHAR_NULL) { |
| // |
| // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset |
| // |
| break; |
| } |
| |
| switch (Key.UnicodeChar) { |
| case CHAR_CARRIAGE_RETURN: |
| ScreenOperation = UiSelect; |
| gDirection = 0; |
| break; |
| |
| // |
| // We will push the adjustment of these numeric values directly to the input handler |
| // NOTE: we won't handle manual input numeric |
| // |
| case '+': |
| case '-': |
| 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); |
| SafeFreePool (OptionString); |
| } |
| break; |
| |
| case '^': |
| ScreenOperation = UiUp; |
| break; |
| |
| case 'V': |
| case 'v': |
| ScreenOperation = UiDown; |
| break; |
| |
| case ' ': |
| if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) { |
| if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) { |
| ScreenOperation = UiSelect; |
| } |
| } |
| break; |
| |
| case CHAR_NULL: |
| if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) || |
| ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) || |
| ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) || |
| ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN)) |
| ) { |
| // |
| // If the function key has been disabled, just ignore the key. |
| // |
| } else { |
| for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) { |
| if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) { |
| if (Key.ScanCode == SCAN_F9) { |
| // |
| // Reset to standard default |
| // |
| DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD; |
| } |
| ScreenOperation = gScanCodeToOperation[Index].ScreenOperation; |
| break; |
| } |
| } |
| } |
| break; |
| } |
| break; |
| |
| case CfScreenOperation: |
| if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) { |
| // |
| // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset |
| // ignore the selection and go back to reading keys. |
| // |
| if (IsListEmpty (&Menu)) { |
| ControlFlag = CfReadKey; |
| break; |
| } |
| // |
| // if there is nothing logical to place a cursor on, just move on to wait for a key. |
| // |
| for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) { |
| NextMenuOption = MENU_OPTION_FROM_LINK (Link); |
| if (IsSelectable (NextMenuOption)) { |
| break; |
| } |
| } |
| |
| if (Link == &Menu) { |
| ControlFlag = CfPrepareToReadKey; |
| break; |
| } |
| } else if (ScreenOperation == UiReset) { |
| // |
| // Press ESC to exit FormSet |
| // |
| Selection->Action = UI_ACTION_EXIT; |
| Selection->Statement = NULL; |
| } |
| |
| for (Index = 0; |
| Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]); |
| Index++ |
| ) { |
| if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) { |
| ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag; |
| break; |
| } |
| } |
| break; |
| |
| case CfUiPrevious: |
| ControlFlag = CfCheckSelection; |
| |
| if (IsListEmpty (&gMenuList)) { |
| Selection->Action = UI_ACTION_NONE; |
| if (IsListEmpty (&Menu)) { |
| ControlFlag = CfReadKey; |
| } |
| break; |
| } |
| |
| // |
| // Remove the Cached page entry |
| // |
| UiRemoveMenuListEntry (Selection); |
| |
| Selection->Action = UI_ACTION_REFRESH_FORM; |
| Selection->Statement = NULL; |
| break; |
| |
| case CfUiSelect: |
| ControlFlag = CfCheckSelection; |
| |
| Statement = MenuOption->ThisTag; |
| if ((Statement->Operand == EFI_IFR_TEXT_OP) || |
| (Statement->Operand == EFI_IFR_DATE_OP) || |
| (Statement->Operand == EFI_IFR_TIME_OP) || |
| (Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0)) { |
| break; |
| } |
| |
| // |
| // Keep highlight on current MenuOption |
| // |
| Selection->QuestionId = Statement->QuestionId; |
| |
| switch (Statement->Operand) { |
| case EFI_IFR_REF_OP: |
| if (Statement->RefDevicePath != 0) { |
| // |
| // Goto another Hii Package list |
| // |
| ControlFlag = CfUiReset; |
| Selection->Action = UI_ACTION_REFRESH_FORMSET; |
| |
| StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle); |
| if (StringPtr == NULL) { |
| // |
| // No device path string not found, exit |
| // |
| Selection->Action = UI_ACTION_EXIT; |
| Selection->Statement = NULL; |
| break; |
| } |
| BufferSize = StrLen (StringPtr) / 4; |
| DevicePath = AllocatePool (BufferSize); |
| |
| HexStringToBuffer ((UINT8 *) DevicePath, &BufferSize, StringPtr); |
| Selection->Handle = DevicePathToHiiHandle (mHiiDatabase, DevicePath); |
| if (Selection->Handle == NULL) { |
| // |
| // If target Hii Handle not found, exit |
| // |
| Selection->Action = UI_ACTION_EXIT; |
| Selection->Statement = NULL; |
| break; |
| } |
| |
| gBS->FreePool (StringPtr); |
| gBS->FreePool (DevicePath); |
| |
| CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID)); |
| Selection->FormId = Statement->RefFormId; |
| Selection->QuestionId = Statement->RefQuestionId; |
| } else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) { |
| // |
| // Goto another Formset, check for uncommitted data |
| // |
| ControlFlag = CfUiReset; |
| Selection->Action = UI_ACTION_REFRESH_FORMSET; |
| |
| CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID)); |
| Selection->FormId = Statement->RefFormId; |
| Selection->QuestionId = Statement->RefQuestionId; |
| } else if (Statement->RefFormId != 0) { |
| // |
| // Goto another form inside this formset, |
| // |
| Selection->Action = UI_ACTION_REFRESH_FORM; |
| |
| // |
| // Link current form so that we can always go back when someone hits the UiPrevious |
| // |
| UiAddMenuListEntry (Selection); |
| |
| Selection->FormId = Statement->RefFormId; |
| Selection->QuestionId = Statement->RefQuestionId; |
| } else if (Statement->RefQuestionId != 0) { |
| // |
| // Goto another Question |
| // |
| Selection->QuestionId = Statement->RefQuestionId; |
| |
| if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK)) { |
| Selection->Action = UI_ACTION_REFRESH_FORM; |
| } else { |
| Repaint = TRUE; |
| NewLine = TRUE; |
| break; |
| } |
| } |
| 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 (MenuOption, TRUE); |
| Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString); |
| |
| if (EFI_ERROR (Status)) { |
| Repaint = TRUE; |
| NewLine = TRUE; |
| break; |
| } |
| |
| if (OptionString != NULL) { |
| PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString); |
| gBS->FreePool (OptionString); |
| } |
| |
| Selection->Action = UI_ACTION_REFRESH_FORM; |
| break; |
| } |
| break; |
| |
| case CfUiReset: |
| // |
| // We are going to leave current FormSet, so check uncommited data in this FormSet |
| // |
| ControlFlag = CfCheckSelection; |
| |
| if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) { |
| // |
| // There is no parent menu for FrontPage |
| // |
| Selection->Action = UI_ACTION_NONE; |
| Selection->Statement = MenuOption->ThisTag; |
| break; |
| } |
| |
| // |
| // If NV flag is up, prompt user |
| // |
| if (gNvUpdateRequired) { |
| Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); |
| |
| YesResponse = gYesResponse[0]; |
| NoResponse = gNoResponse[0]; |
| |
| do { |
| CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString); |
| } while |
| ( |
| (Key.ScanCode != SCAN_ESC) && |
| ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) && |
| ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET)) |
| ); |
| |
| // |
| // If the user hits the YesResponse key |
| // |
| if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) { |
| } else { |
| Repaint = TRUE; |
| NewLine = TRUE; |
| |
| Selection->Action = UI_ACTION_NONE; |
| break; |
| } |
| } |
| |
| gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); |
| gST->ConOut->EnableCursor (gST->ConOut, TRUE); |
| |
| UiFreeMenuList (); |
| gST->ConOut->ClearScreen (gST->ConOut); |
| return EFI_SUCCESS; |
| |
| case CfUiLeft: |
| ControlFlag = CfCheckSelection; |
| 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. |
| // |
| NewPos = NewPos->BackLink; |
| } |
| } |
| break; |
| |
| case CfUiRight: |
| ControlFlag = CfCheckSelection; |
| 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. |
| // |
| NewPos = NewPos->ForwardLink; |
| } |
| } |
| break; |
| |
| case CfUiUp: |
| ControlFlag = CfCheckSelection; |
| |
| SavedListEntry = TopOfScreen; |
| |
| if (NewPos->BackLink != &Menu) { |
| NewLine = TRUE; |
| // |
| // Adjust Date/Time position before we advance forward. |
| // |
| AdjustDateAndTimePosition (TRUE, &NewPos); |
| |
| // |
| // Caution that we have already rewind to the top, don't go backward in this situation. |
| // |
| if (NewPos->BackLink != &Menu) { |
| NewPos = NewPos->BackLink; |
| } |
| |
| PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| DistanceValue = PreviousMenuOption->Skip; |
| |
| // |
| // Since the behavior of hitting the up arrow on a Date/Time op-code is intended |
| // to be one that back to the previous set of op-codes, we need to advance to the sencond |
| // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate |
| // checking can be done. |
| // |
| DistanceValue += AdjustDateAndTimePosition (TRUE, &NewPos); |
| |
| // |
| // Check the previous menu entry to see if it was a zero-length advance. If it was, |
| // don't worry about a redraw. |
| // |
| if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) { |
| Repaint = TRUE; |
| TopOfScreen = NewPos; |
| } |
| |
| Difference = MoveToNextStatement (TRUE, &NewPos); |
| if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) { |
| if (Difference > 0) { |
| // |
| // Previous focus MenuOption is above the TopOfScreen, so we need to scroll |
| // |
| TopOfScreen = NewPos; |
| Repaint = TRUE; |
| } |
| } |
| if (Difference < 0) { |
| // |
| // We want to goto previous MenuOption, but finally we go down. |
| // it means that we hit the begining MenuOption that can be focused |
| // so we simply scroll to the top |
| // |
| if (SavedListEntry != Menu.ForwardLink) { |
| TopOfScreen = Menu.ForwardLink; |
| Repaint = TRUE; |
| } |
| } |
| |
| // |
| // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. |
| // |
| AdjustDateAndTimePosition (TRUE, &TopOfScreen); |
| |
| UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE); |
| } else { |
| SavedMenuOption = MenuOption; |
| MenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| if (!IsSelectable (MenuOption)) { |
| // |
| // If we are at the end of the list and sitting on a text op, we need to more forward |
| // |
| ScreenOperation = UiDown; |
| ControlFlag = CfScreenOperation; |
| break; |
| } |
| |
| MenuOption = SavedMenuOption; |
| } |
| break; |
| |
| case CfUiPageUp: |
| ControlFlag = CfCheckSelection; |
| |
| if (NewPos->BackLink == &Menu) { |
| NewLine = FALSE; |
| Repaint = FALSE; |
| break; |
| } |
| |
| NewLine = TRUE; |
| Repaint = TRUE; |
| Link = TopOfScreen; |
| PreviousMenuOption = MENU_OPTION_FROM_LINK (Link); |
| Index = BottomRow; |
| while ((Index >= TopRow) && (Link->BackLink != &Menu)) { |
| Index = Index - PreviousMenuOption->Skip; |
| Link = Link->BackLink; |
| PreviousMenuOption = MENU_OPTION_FROM_LINK (Link); |
| } |
| |
| TopOfScreen = Link; |
| Difference = MoveToNextStatement (TRUE, &Link); |
| if (Difference > 0) { |
| // |
| // The focus MenuOption is above the TopOfScreen |
| // |
| TopOfScreen = Link; |
| } else if (Difference < 0) { |
| // |
| // This happens when there is no MenuOption can be focused from |
| // Current MenuOption to the first MenuOption |
| // |
| TopOfScreen = Menu.ForwardLink; |
| } |
| Index += Difference; |
| if (Index < TopRow) { |
| MenuOption = NULL; |
| } |
| |
| if (NewPos == Link) { |
| Repaint = FALSE; |
| NewLine = FALSE; |
| } else { |
| NewPos = Link; |
| } |
| |
| // |
| // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. |
| // Don't do this when we are already in the first page. |
| // |
| AdjustDateAndTimePosition (TRUE, &TopOfScreen); |
| AdjustDateAndTimePosition (TRUE, &NewPos); |
| break; |
| |
| case CfUiPageDown: |
| ControlFlag = CfCheckSelection; |
| |
| if (NewPos->ForwardLink == &Menu) { |
| NewLine = FALSE; |
| Repaint = FALSE; |
| break; |
| } |
| |
| NewLine = TRUE; |
| Repaint = TRUE; |
| Link = TopOfScreen; |
| NextMenuOption = MENU_OPTION_FROM_LINK (Link); |
| Index = TopRow; |
| while ((Index <= BottomRow) && (Link->ForwardLink != &Menu)) { |
| Index = Index + NextMenuOption->Skip; |
| Link = Link->ForwardLink; |
| NextMenuOption = MENU_OPTION_FROM_LINK (Link); |
| } |
| |
| Index += MoveToNextStatement (FALSE, &Link); |
| if (Index > BottomRow) { |
| // |
| // There are more MenuOption needing scrolling |
| // |
| TopOfScreen = Link; |
| MenuOption = NULL; |
| } |
| if (NewPos == Link && Index <= BottomRow) { |
| // |
| // Finally we know that NewPos is the last MenuOption can be focused. |
| // |
| NewLine = FALSE; |
| Repaint = FALSE; |
| } else { |
| NewPos = Link; |
| } |
| |
| // |
| // If we encounter a Date/Time op-code set, rewind to the first op-code of the set. |
| // Don't do this when we are already in the last page. |
| // |
| AdjustDateAndTimePosition (TRUE, &TopOfScreen); |
| AdjustDateAndTimePosition (TRUE, &NewPos); |
| break; |
| |
| case CfUiDown: |
| 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; |
| DistanceValue = AdjustDateAndTimePosition (FALSE, &NewPos); |
| |
| if (NewPos->ForwardLink != &Menu) { |
| MenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| NewLine = TRUE; |
| NewPos = NewPos->ForwardLink; |
| NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| |
| DistanceValue += NextMenuOption->Skip; |
| DistanceValue += MoveToNextStatement (FALSE, &NewPos); |
| // |
| // An option might be multi-line, so we need to reflect that data in the overall skip value |
| // |
| UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, SkipValue); |
| |
| 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 |
| // |
| for (; |
| Difference >= (INTN) SavedMenuOption->Skip; |
| Difference = Difference - (INTN) SavedMenuOption->Skip |
| ) { |
| // |
| // Since the Difference is greater than or equal to this op-code's skip value, skip it |
| // |
| TopOfScreen = TopOfScreen->ForwardLink; |
| SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); |
| if (Difference < (INTN) SavedMenuOption->Skip) { |
| Difference = SavedMenuOption->Skip - Difference - 1; |
| break; |
| } else { |
| if (Difference == (INTN) SavedMenuOption->Skip) { |
| TopOfScreen = TopOfScreen->ForwardLink; |
| SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); |
| Difference = SavedMenuOption->Skip - Difference; |
| break; |
| } |
| } |
| } |
| // |
| // 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 { |
| SkipValue = 0; |
| TopOfScreen = TopOfScreen->ForwardLink; |
| } |
| } while (SavedMenuOption->Skip == 0); |
| |
| Repaint = TRUE; |
| OldSkipValue = SkipValue; |
| } |
| |
| MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry); |
| |
| UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE); |
| |
| } else { |
| SavedMenuOption = MenuOption; |
| MenuOption = MENU_OPTION_FROM_LINK (NewPos); |
| if (!IsSelectable (MenuOption)) { |
| // |
| // If we are at the end of the list and sitting on a text op, we need to more forward |
| // |
| ScreenOperation = UiUp; |
| ControlFlag = CfScreenOperation; |
| break; |
| } |
| |
| MenuOption = SavedMenuOption; |
| // |
| // If we are at the end of the list and sitting on a Date/Time op, rewind to the head. |
| // |
| AdjustDateAndTimePosition (TRUE, &NewPos); |
| } |
| break; |
| |
| case CfUiSave: |
| ControlFlag = CfCheckSelection; |
| |
| // |
| // Submit the form |
| // |
| Status = SubmitForm (Selection->FormSet, Selection->Form); |
| |
| if (!EFI_ERROR (Status)) { |
| UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE); |
| UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE); |
| } else { |
| do { |
| CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString); |
| } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); |
| |
| Repaint = TRUE; |
| NewLine = TRUE; |
| } |
| break; |
| |
| case CfUiDefault: |
| ControlFlag = CfCheckSelection; |
| |
| Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId); |
| |
| if (!EFI_ERROR (Status)) { |
| Selection->Action = UI_ACTION_REFRESH_FORM; |
| |
| // |
| // Show NV update flag on status bar |
| // |
| gNvUpdateRequired = 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; |
| } |
| } |
| } |