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