2 Copyright (c) 2006 - 2007, Intel Corporation
3 All rights reserved. This program and the accompanying materials
4 are licensed and made available under the terms and conditions of the BSD License
5 which accompanies this distribution. The full text of the license may be found at
6 http://opensource.org/licenses/bsd-license.php
8 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
9 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 Some presentation routines.
23 // Include common header file for this module.
25 #include "CommonHeader.h"
44 // For now, allocate an arbitrarily long buffer
46 Buffer
= AllocateZeroPool (0x10000);
47 ASSERT (Buffer
!= NULL
);
50 // Set foreground and background as defined
52 gST
->ConOut
->SetAttribute (gST
->ConOut
, TextAttribute
);
55 // Much faster to buffer the long string instead of print it a character at a time
57 SetUnicodeMem (Buffer
, RightColumn
- LeftColumn
, L
' ');
60 // Clear the desired area with the appropriate foreground/background
62 for (Row
= TopRow
; Row
<= BottomRow
; Row
++) {
63 PrintStringAt (LeftColumn
, Row
, Buffer
);
66 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, LeftColumn
, TopRow
);
80 for (Length
= 0; Destination
[Length
] != 0; Length
++)
84 // We now have the length of the original string
85 // We can safely assume for now that we are concatenating a narrow value to this string.
86 // For instance, the string is "XYZ" and cat'ing ">"
87 // If this assumption changes, we need to make this routine a bit more complex
89 Destination
[Length
] = (CHAR16
) NARROW_CHAR
;
92 StrCpy (Destination
+ Length
, Source
);
102 UINTN IncrementValue
;
110 // Advance to the null-terminator or to the first width directive
113 (String
[Index
] != NARROW_CHAR
) && (String
[Index
] != WIDE_CHAR
) && (String
[Index
] != 0);
114 Index
++, Count
= Count
+ IncrementValue
119 // We hit the null-terminator, we now have a count
121 if (String
[Index
] == 0) {
125 // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
126 // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
128 if (String
[Index
] == NARROW_CHAR
) {
130 // Skip to the next character
136 // Skip to the next character
141 } while (String
[Index
] != 0);
144 // Increment by one to include the null-terminator in the size
148 return Count
* sizeof (CHAR16
);
161 CHAR16
*StrFrontPageBanner
;
162 EFI_SCREEN_DESCRIPTOR LocalScreen
;
165 ZeroMem (&LocalScreen
, sizeof (EFI_SCREEN_DESCRIPTOR
));
166 gST
->ConOut
->QueryMode (gST
->ConOut
, gST
->ConOut
->Mode
->Mode
, &LocalScreen
.RightColumn
, &LocalScreen
.BottomRow
);
167 ClearLines (0, LocalScreen
.RightColumn
, 0, LocalScreen
.BottomRow
, KEYHELP_BACKGROUND
);
169 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
172 // For now, allocate an arbitrarily long buffer
174 Buffer
= AllocateZeroPool (0x10000);
175 ASSERT (Buffer
!= NULL
);
177 Character
= (CHAR16
) BOXDRAW_HORIZONTAL
;
179 for (Index
= 0; Index
+ 2 < (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
); Index
++) {
180 Buffer
[Index
] = Character
;
183 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
185 // ClearLines(0, LocalScreen.RightColumn, 0, BANNER_HEIGHT-1, BANNER_TEXT | BANNER_BACKGROUND);
188 LocalScreen
.LeftColumn
,
189 LocalScreen
.RightColumn
,
191 FRONT_PAGE_HEADER_HEIGHT
- 1 + LocalScreen
.TopRow
,
192 BANNER_TEXT
| BANNER_BACKGROUND
195 // for (Line = 0; Line < BANNER_HEIGHT; Line++) {
197 for (Line
= (UINT8
) LocalScreen
.TopRow
; Line
< BANNER_HEIGHT
+ (UINT8
) LocalScreen
.TopRow
; Line
++) {
199 // for (Alignment = 0; Alignment < BANNER_COLUMNS; Alignment++) {
201 for (Alignment
= (UINT8
) LocalScreen
.LeftColumn
;
202 Alignment
< BANNER_COLUMNS
+ (UINT8
) LocalScreen
.LeftColumn
;
205 if (BannerData
->Banner
[Line
- (UINT8
) LocalScreen
.TopRow
][Alignment
- (UINT8
) LocalScreen
.LeftColumn
] != 0x0000) {
206 StrFrontPageBanner
= GetToken (
207 BannerData
->Banner
[Line
- (UINT8
) LocalScreen
.TopRow
][Alignment
- (UINT8
) LocalScreen
.LeftColumn
],
214 switch (Alignment
- LocalScreen
.LeftColumn
) {
217 // Handle left column
219 PrintStringAt (LocalScreen
.LeftColumn
, Line
, StrFrontPageBanner
);
224 // Handle center column
227 LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 3,
235 // Handle right column
238 LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) * 2 / 3,
245 FreePool (StrFrontPageBanner
);
251 LocalScreen
.LeftColumn
,
252 LocalScreen
.RightColumn
,
253 LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
,
254 LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- 1,
255 KEYHELP_TEXT
| KEYHELP_BACKGROUND
258 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
260 LocalScreen
.LeftColumn
,
261 LocalScreen
.RightColumn
,
263 LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
- 1,
264 TITLE_TEXT
| TITLE_BACKGROUND
267 // Print Top border line
268 // +------------------------------------------------------------------------------+
270 // +------------------------------------------------------------------------------+
272 Character
= (CHAR16
) BOXDRAW_DOWN_RIGHT
;
274 PrintChar (Character
);
275 PrintString (Buffer
);
277 Character
= (CHAR16
) BOXDRAW_DOWN_LEFT
;
278 PrintChar (Character
);
280 Character
= (CHAR16
) BOXDRAW_VERTICAL
;
281 for (Row
= LocalScreen
.TopRow
+ 1; Row
<= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
- 2; Row
++) {
282 PrintCharAt (LocalScreen
.LeftColumn
, Row
, Character
);
283 PrintCharAt (LocalScreen
.RightColumn
- 1, Row
, Character
);
286 Character
= (CHAR16
) BOXDRAW_UP_RIGHT
;
287 PrintCharAt (LocalScreen
.LeftColumn
, LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
- 1, Character
);
288 PrintString (Buffer
);
290 Character
= (CHAR16
) BOXDRAW_UP_LEFT
;
291 PrintChar (Character
);
293 if (gClassOfVfr
== EFI_SETUP_APPLICATION_SUBCLASS
) {
295 // Print Bottom border line
296 // +------------------------------------------------------------------------------+
298 // +------------------------------------------------------------------------------+
300 Character
= (CHAR16
) BOXDRAW_DOWN_RIGHT
;
301 PrintCharAt (LocalScreen
.LeftColumn
, LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
, Character
);
303 PrintString (Buffer
);
305 Character
= (CHAR16
) BOXDRAW_DOWN_LEFT
;
306 PrintChar (Character
);
307 Character
= (CHAR16
) BOXDRAW_VERTICAL
;
308 for (Row
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
+ 1;
309 Row
<= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- 2;
312 PrintCharAt (LocalScreen
.LeftColumn
, Row
, Character
);
313 PrintCharAt (LocalScreen
.RightColumn
- 1, Row
, Character
);
316 Character
= (CHAR16
) BOXDRAW_UP_RIGHT
;
317 PrintCharAt (LocalScreen
.LeftColumn
, LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- 1, Character
);
319 PrintString (Buffer
);
321 Character
= (CHAR16
) BOXDRAW_UP_LEFT
;
322 PrintChar (Character
);
331 +------------------------------------------------------------------------------+
332 ?F2=Previous Page Setup Page ?
333 +------------------------------------------------------------------------------+
351 +------------------------------------------------------------------------------+
352 ?F1=Scroll Help F9=Reset to Defaults F10=Save and Exit ?
353 | ^"=Move Highlight <Spacebar> Toggles Checkbox Esc=Discard Changes |
354 +------------------------------------------------------------------------------+
359 OUT UI_MENU_OPTION
*Selection
,
360 IN UINT16 FormHandle
,
361 IN UINT16 TitleToken
,
362 IN EFI_FORM_TAGS FormTags
,
363 IN EFI_FILE_FORM_TAGS
*FileFormTagsHead
,
364 IN UINT8
*CallbackData
370 UINT16 MenuItemCount
;
371 EFI_HII_HANDLE Handle
;
374 EFI_FILE_FORM_TAGS
*FileFormTags
;
379 EFI_SCREEN_DESCRIPTOR LocalScreen
;
382 CHAR16
*OutputString
;
384 Handle
= Selection
->Handle
;
391 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
394 // If we hit a F2 (previous) we already nuked the menu and are simply carrying around what information we need
396 if (Selection
->Previous
) {
397 Selection
->Previous
= FALSE
;
403 StringPtr
= GetToken (TitleToken
, Handle
);
405 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
406 gST
->ConOut
->SetAttribute (gST
->ConOut
, TITLE_TEXT
| TITLE_BACKGROUND
);
408 (LocalScreen
.RightColumn
+ LocalScreen
.LeftColumn
- GetStringWidth (StringPtr
) / 2) / 2,
409 LocalScreen
.TopRow
+ 1,
414 if (gClassOfVfr
== EFI_SETUP_APPLICATION_SUBCLASS
) {
415 gST
->ConOut
->SetAttribute (gST
->ConOut
, KEYHELP_TEXT
| KEYHELP_BACKGROUND
);
418 // Display the infrastructure strings
420 if (!IsListEmpty (&gMenuList
)) {
421 PrintStringAt (LocalScreen
.LeftColumn
+ 2, LocalScreen
.TopRow
+ 1, gFunctionTwoString
);
424 PrintStringAt (LocalScreen
.LeftColumn
+ 2, LocalScreen
.BottomRow
- 4, gFunctionOneString
);
426 LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 3,
427 LocalScreen
.BottomRow
- 4,
431 LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) * 2 / 3,
432 LocalScreen
.BottomRow
- 4,
435 PrintAt (LocalScreen
.LeftColumn
+ 2, LocalScreen
.BottomRow
- 3, (CHAR16
*) L
"%c%c%s", ARROW_UP
, ARROW_DOWN
, gMoveHighlight
);
437 LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 3,
438 LocalScreen
.BottomRow
- 3,
443 // Remove Buffer allocated for StringPtr after it has been used.
445 FreePool (StringPtr
);
447 for (Index
= 0; FormTags
.Tags
[Index
].Operand
!= EFI_IFR_END_FORM_OP
; Index
++) {
452 FileFormTags
= FileFormTagsHead
;
454 if (FormTags
.Tags
[Index
].Operand
== EFI_IFR_FORM_OP
) {
455 FormId
= FormTags
.Tags
[Index
].Id
;
458 // This gives us visibility to the FileFormTags->NvRamMap to check things
459 // ActiveIfr is a global maintained by the menuing code to ensure that we
460 // are pointing to the correct formset's file data.
462 for (Count
= 0; Count
< gActiveIfr
; Count
++) {
463 FileFormTags
= FileFormTags
->NextFile
;
466 // GrayoutIf [SuppressIf]
471 // SuppressIf [GrayoutIf]
479 switch (FormTags
.Tags
[Index
].Operand
) {
480 case EFI_IFR_SUPPRESS_IF_OP
:
483 case EFI_IFR_GRAYOUT_IF_OP
:
488 // Advance to the next op-code
493 // We are now pointing to the beginning of the consistency checking. Let's fast forward
494 // through the AND/OR/NOT data to come up with some meaningful ID data.
497 FormTags
.Tags
[Index
].Operand
== EFI_IFR_AND_OP
||
498 FormTags
.Tags
[Index
].Operand
== EFI_IFR_OR_OP
||
499 FormTags
.Tags
[Index
].Operand
== EFI_IFR_GT_OP
||
500 FormTags
.Tags
[Index
].Operand
== EFI_IFR_GE_OP
||
501 FormTags
.Tags
[Index
].Operand
== EFI_IFR_NOT_OP
;
507 // We need to walk through the consistency checks until we hit the end of the consistency
508 // FALSE means evaluate this single expression
509 // The ConsistencyId refers to which expression in the Consistency database to use
512 Suppress
= ValueIsNotValid (
514 FormTags
.Tags
[Index
].ConsistencyId
,
515 &FormTags
.Tags
[Index
],
521 GrayOut
= ValueIsNotValid (
523 FormTags
.Tags
[Index
].ConsistencyId
,
524 &FormTags
.Tags
[Index
],
530 // Advance to the end of the expression (Will land us at a grayoutif/suppressif or the op-code being affected)
533 FormTags
.Tags
[Index
].Operand
== EFI_IFR_EQ_ID_VAL_OP
||
534 FormTags
.Tags
[Index
].Operand
== EFI_IFR_EQ_VAR_VAL_OP
||
535 FormTags
.Tags
[Index
].Operand
== EFI_IFR_EQ_ID_ID_OP
||
536 FormTags
.Tags
[Index
].Operand
== EFI_IFR_EQ_ID_LIST_OP
||
537 FormTags
.Tags
[Index
].Operand
== EFI_IFR_NOT_OP
||
538 FormTags
.Tags
[Index
].Operand
== EFI_IFR_AND_OP
||
539 FormTags
.Tags
[Index
].Operand
== EFI_IFR_OR_OP
||
540 FormTags
.Tags
[Index
].Operand
== EFI_IFR_TRUE_OP
||
541 FormTags
.Tags
[Index
].Operand
== EFI_IFR_FALSE_OP
||
542 FormTags
.Tags
[Index
].Operand
== EFI_IFR_GT_OP
||
543 FormTags
.Tags
[Index
].Operand
== EFI_IFR_GE_OP
||
544 FormTags
.Tags
[Index
].Operand
== EFI_IFR_LABEL_OP
;
554 // Do this two times (at most will see a suppress and grayout combination
562 FormTags
.Tags
[Index
].GrayOut
= TRUE
;
564 FormTags
.Tags
[Index
].GrayOut
= FALSE
;
566 if (Suppress
&& FormTags
.Tags
[Index
].Operand
== EFI_IFR_ONE_OF_OPTION_OP
) {
568 // Only need .Suppress field when the tag is a one_of_option. For other cases, omit them directly.
570 FormTags
.Tags
[Index
].Suppress
= TRUE
;
572 FormTags
.Tags
[Index
].Suppress
= FALSE
;
576 FormTags
.Tags
[Index
].NumberOfLines
> 0 ||
577 FormTags
.Tags
[Index
].Operand
== EFI_IFR_DATE_OP
||
578 FormTags
.Tags
[Index
].Operand
== EFI_IFR_TIME_OP
583 StringPtr
= GetToken (FormTags
.Tags
[Index
].Text
, Handle
);
585 Width
= GetWidth (&FormTags
.Tags
[Index
], Handle
);
588 // This data can be retrieved over and over again. Therefore, reset to original values
589 // before processing otherwise things will start growing linearly
591 if (FormTags
.Tags
[Index
].NumberOfLines
> 1) {
592 FormTags
.Tags
[Index
].NumberOfLines
= 1;
595 for (Count
= 0; GetLineByWidth (StringPtr
, Width
, &ArrayEntry
, &OutputString
) != 0x0000;) {
597 // If there is more string to process print on the next row and increment the Skip value
599 if (StrLen (&StringPtr
[ArrayEntry
])) {
600 FormTags
.Tags
[Index
].NumberOfLines
++;
603 FreePool (OutputString
);
609 // We are NOT!! removing this StringPtr buffer via FreePool since it is being used in the menuoptions, we will do
612 UiAddSubMenuOption (StringPtr
, Handle
, FormTags
.Tags
, Index
, FormId
, MenuItemCount
);
616 // Keep processing menu entries based on the resultant suppress/grayout results until we hit an end-if
619 } while (FormTags
.Tags
[Index
].Operand
!= EFI_IFR_END_IF_OP
&& Conditional
);
622 // We advanced the index for the above conditional, rewind it to keep harmony with the for loop logic
627 Selection
= UiDisplayMenu (TRUE
, FileFormTagsHead
, (EFI_IFR_DATA_ARRAY
*) CallbackData
);
633 InitializeBrowserStrings (
637 gFunctionOneString
= GetToken (STRING_TOKEN (FUNCTION_ONE_STRING
), gHiiHandle
);
638 gFunctionTwoString
= GetToken (STRING_TOKEN (FUNCTION_TWO_STRING
), gHiiHandle
);
639 gFunctionNineString
= GetToken (STRING_TOKEN (FUNCTION_NINE_STRING
), gHiiHandle
);
640 gFunctionTenString
= GetToken (STRING_TOKEN (FUNCTION_TEN_STRING
), gHiiHandle
);
641 gEnterString
= GetToken (STRING_TOKEN (ENTER_STRING
), gHiiHandle
);
642 gEnterCommitString
= GetToken (STRING_TOKEN (ENTER_COMMIT_STRING
), gHiiHandle
);
643 gEscapeString
= GetToken (STRING_TOKEN (ESCAPE_STRING
), gHiiHandle
);
644 gMoveHighlight
= GetToken (STRING_TOKEN (MOVE_HIGHLIGHT
), gHiiHandle
);
645 gMakeSelection
= GetToken (STRING_TOKEN (MAKE_SELECTION
), gHiiHandle
);
646 gNumericInput
= GetToken (STRING_TOKEN (NUMERIC_INPUT
), gHiiHandle
);
647 gToggleCheckBox
= GetToken (STRING_TOKEN (TOGGLE_CHECK_BOX
), gHiiHandle
);
648 gPromptForPassword
= GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD
), gHiiHandle
);
649 gPromptForNewPassword
= GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD
), gHiiHandle
);
650 gConfirmPassword
= GetToken (STRING_TOKEN (CONFIRM_PASSWORD
), gHiiHandle
);
651 gConfirmError
= GetToken (STRING_TOKEN (CONFIRM_ERROR
), gHiiHandle
);
652 gPressEnter
= GetToken (STRING_TOKEN (PRESS_ENTER
), gHiiHandle
);
653 gEmptyString
= GetToken (STRING_TOKEN (EMPTY_STRING
), gHiiHandle
);
654 gAreYouSure
= GetToken (STRING_TOKEN (ARE_YOU_SURE
), gHiiHandle
);
655 gYesResponse
= GetToken (STRING_TOKEN (ARE_YOU_SURE_YES
), gHiiHandle
);
656 gNoResponse
= GetToken (STRING_TOKEN (ARE_YOU_SURE_NO
), gHiiHandle
);
657 gMiniString
= GetToken (STRING_TOKEN (MINI_STRING
), gHiiHandle
);
658 gPlusString
= GetToken (STRING_TOKEN (PLUS_STRING
), gHiiHandle
);
659 gMinusString
= GetToken (STRING_TOKEN (MINUS_STRING
), gHiiHandle
);
660 gAdjustNumber
= GetToken (STRING_TOKEN (ADJUST_NUMBER
), gHiiHandle
);
666 IN UI_MENU_OPTION
*Selection
,
671 Update key's help imformation
674 Selection C The form that current display
675 Selected C Whether or not a tag be selected
683 UINTN LeftColumnOfHelp
;
684 UINTN RightColumnOfHelp
;
686 UINTN BottomRowOfHelp
;
687 UINTN StartColumnOfHelp
;
688 EFI_SCREEN_DESCRIPTOR LocalScreen
;
690 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
692 SecCol
= LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 3;
693 ThdCol
= LocalScreen
.LeftColumn
+ (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) * 2 / 3;
695 StartColumnOfHelp
= LocalScreen
.LeftColumn
+ 2;
696 LeftColumnOfHelp
= LocalScreen
.LeftColumn
+ 1;
697 RightColumnOfHelp
= LocalScreen
.RightColumn
- 2;
698 TopRowOfHelp
= LocalScreen
.BottomRow
- 4;
699 BottomRowOfHelp
= LocalScreen
.BottomRow
- 3;
701 if (gClassOfVfr
== EFI_GENERAL_APPLICATION_SUBCLASS
) {
705 gST
->ConOut
->SetAttribute (gST
->ConOut
, KEYHELP_TEXT
| KEYHELP_BACKGROUND
);
707 switch (Selection
->ThisTag
->Operand
) {
708 case EFI_IFR_ORDERED_LIST_OP
:
709 case EFI_IFR_ONE_OF_OP
:
710 case EFI_IFR_NUMERIC_OP
:
711 case EFI_IFR_TIME_OP
:
712 case EFI_IFR_DATE_OP
:
713 ClearLines (LeftColumnOfHelp
, RightColumnOfHelp
, TopRowOfHelp
, BottomRowOfHelp
, KEYHELP_TEXT
| KEYHELP_BACKGROUND
);
716 if (gClassOfVfr
== EFI_SETUP_APPLICATION_SUBCLASS
) {
717 PrintStringAt (StartColumnOfHelp
, TopRowOfHelp
, gFunctionOneString
);
718 PrintStringAt (SecCol
, TopRowOfHelp
, gFunctionNineString
);
719 PrintStringAt (ThdCol
, TopRowOfHelp
, gFunctionTenString
);
720 PrintStringAt (ThdCol
, BottomRowOfHelp
, gEscapeString
);
724 if ((Selection
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (Selection
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
728 (CHAR16
*) L
"%c%c%c%c%s",
735 PrintStringAt (SecCol
, BottomRowOfHelp
, gAdjustNumber
);
737 PrintAt (StartColumnOfHelp
, BottomRowOfHelp
, (CHAR16
*) L
"%c%c%s", ARROW_UP
, ARROW_DOWN
, gMoveHighlight
);
738 PrintStringAt (SecCol
, BottomRowOfHelp
, gEnterString
);
741 PrintStringAt (SecCol
, BottomRowOfHelp
, gEnterCommitString
);
744 // If it is a selected numeric with manual input, display different message
746 if ((Selection
->ThisTag
->Operand
== EFI_IFR_NUMERIC_OP
) && (Selection
->ThisTag
->Step
== 0)) {
747 PrintStringAt (SecCol
, TopRowOfHelp
, gNumericInput
);
748 } else if (Selection
->ThisTag
->Operand
!= EFI_IFR_ORDERED_LIST_OP
) {
749 PrintAt (StartColumnOfHelp
, BottomRowOfHelp
, L
"%c%c%s", ARROW_UP
, ARROW_DOWN
, gMoveHighlight
);
752 if (Selection
->ThisTag
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
753 PrintStringAt (StartColumnOfHelp
, TopRowOfHelp
, gPlusString
);
754 PrintStringAt (ThdCol
, TopRowOfHelp
, gMinusString
);
757 PrintStringAt (ThdCol
, BottomRowOfHelp
, gEscapeString
);
761 case EFI_IFR_CHECKBOX_OP
:
762 ClearLines (LeftColumnOfHelp
, RightColumnOfHelp
, TopRowOfHelp
, BottomRowOfHelp
, KEYHELP_TEXT
| KEYHELP_BACKGROUND
);
764 if (gClassOfVfr
== EFI_SETUP_APPLICATION_SUBCLASS
) {
765 PrintStringAt (StartColumnOfHelp
, TopRowOfHelp
, gFunctionOneString
);
766 PrintStringAt (SecCol
, TopRowOfHelp
, gFunctionNineString
);
767 PrintStringAt (ThdCol
, TopRowOfHelp
, gFunctionTenString
);
768 PrintStringAt (ThdCol
, BottomRowOfHelp
, gEscapeString
);
771 PrintAt (StartColumnOfHelp
, BottomRowOfHelp
, (CHAR16
*) L
"%c%c%s", ARROW_UP
, ARROW_DOWN
, gMoveHighlight
);
772 PrintStringAt (SecCol
, BottomRowOfHelp
, gToggleCheckBox
);
776 case EFI_IFR_PASSWORD_OP
:
777 case EFI_IFR_STRING_OP
:
778 ClearLines (LeftColumnOfHelp
, RightColumnOfHelp
, TopRowOfHelp
, BottomRowOfHelp
, KEYHELP_TEXT
| KEYHELP_BACKGROUND
);
781 if (gClassOfVfr
== EFI_SETUP_APPLICATION_SUBCLASS
) {
782 PrintStringAt (StartColumnOfHelp
, TopRowOfHelp
, gFunctionOneString
);
783 PrintStringAt (SecCol
, TopRowOfHelp
, gFunctionNineString
);
784 PrintStringAt (ThdCol
, TopRowOfHelp
, gFunctionTenString
);
785 PrintStringAt (ThdCol
, BottomRowOfHelp
, gEscapeString
);
788 PrintAt (StartColumnOfHelp
, BottomRowOfHelp
, (CHAR16
*) L
"%c%c%s", ARROW_UP
, ARROW_DOWN
, gMoveHighlight
);
789 PrintStringAt (SecCol
, BottomRowOfHelp
, gEnterString
);
791 if (Selection
->ThisTag
->Operand
!= EFI_IFR_REF_OP
) {
793 (LocalScreen
.RightColumn
- GetStringWidth (gEnterCommitString
) / 2) / 2,
797 PrintStringAt (ThdCol
, BottomRowOfHelp
, gEscapeString
);
808 IN UI_MENU_OPTION
*Selection
,
809 IN EFI_FILE_FORM_TAGS
*FileFormTagsHead
,
811 OUT UINT16
*FormHandle
,
812 OUT UINT16
*TitleToken
,
813 OUT EFI_FORM_TAGS
*FormTags
817 EFI_FILE_FORM_TAGS
*FileFormTags
;
818 EFI_FORM_TAGS LocalTags
;
820 FileFormTags
= FileFormTagsHead
;
823 // Advance FileFormTags to the correct file's tag information.
824 // For instance, if Selection->IfrNumber is 3, that means the 4th
825 // file (0-based) in the FileFormTags linked-list contains the tag
828 for (Index
= 0; Index
< Selection
->IfrNumber
; Index
++) {
829 FileFormTags
= FileFormTags
->NextFile
;
832 LocalTags
= FileFormTags
->FormTags
;
836 // Advance Index to the first FormOp tag information
838 for (Index
= 0; FileFormTags
->FormTags
.Tags
[Index
].Operand
!= EFI_IFR_FORM_OP
; Index
++)
842 // Advance Index to the FormOp with the correct ID value
844 for (; LocalTags
.Next
!= NULL
; LocalTags
= *LocalTags
.Next
) {
845 for (Index
= 0; LocalTags
.Tags
[Index
].Operand
!= EFI_IFR_FORM_OP
; Index
++)
847 if (LocalTags
.Tags
[Index
].Id
== IdValue
) {
853 // return the Form Id, Text, and the File's FormTags structure
855 *FormHandle
= LocalTags
.Tags
[Index
].Id
;
856 *TitleToken
= LocalTags
.Tags
[Index
].Text
;
857 *FormTags
= LocalTags
;
865 IN UINT16 ConsistencyId
,
866 IN UINT16 CurrentVariable
,
867 IN EFI_FORM_TAGS
*FormTags
,
868 OUT EFI_FILE_FORM_TAGS
*FileFormTags
873 UINT16 QuestionIndex
;
881 // Initialize some Index variable and Status
887 Status
= EFI_SUCCESS
;
891 // Determine the number of tags for the first form
893 GetTagCount (&FormData
[Index
], &NumberOfTags
);
896 // Allocate memory for our tags on the first form
898 FormTags
->Tags
= AllocateZeroPool (NumberOfTags
* sizeof (EFI_TAG
));
899 ASSERT (FormTags
->Tags
!= NULL
);
901 for (CurrTag
= 0; FormData
[Index
] != EFI_IFR_END_FORM_SET_OP
; CurrTag
++) {
903 // Operand = IFR OpCode
905 FormTags
->Tags
[CurrTag
].Operand
= FormData
[Index
];
908 // Assume for now 0 lines occupied by this OpCode
910 FormTags
->Tags
[CurrTag
].NumberOfLines
= 0;
913 // Determine the length of the Tag so we can later skip to the next tag in the form
918 TagLength
= FormData
[Index
+ 1];
920 // Operate on the Found OpCode
922 switch (FormData
[Index
]) {
924 case EFI_IFR_FORM_OP
:
925 case EFI_IFR_SUBTITLE_OP
:
926 case EFI_IFR_TEXT_OP
:
928 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
931 case EFI_IFR_VARSTORE_SELECT_OP
:
932 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
933 CopyMem (&CurrentVariable
, &((EFI_IFR_VARSTORE_SELECT
*) &FormData
[Index
])->VarId
, sizeof (UINT16
));
936 case EFI_IFR_END_FORM_OP
:
937 FormTags
->Tags
[CurrTag
].Operand
= FormData
[Index
];
938 FormTags
->Tags
[CurrTag
].NumberOfLines
= 0;
943 case EFI_IFR_ORDERED_LIST_OP
:
944 case EFI_IFR_ONE_OF_OP
:
945 GetQuestionHeader (&FormTags
->Tags
[CurrTag
], FormData
, Index
, FileFormTags
, CurrentVariable
);
948 // Store away the CurrTag since what follows will be the answer that we
949 // need to place into the appropriate location in the tag array
952 // record for setting default later
954 QuestionIndex
= (UINT16
) CurrTag
;
957 case EFI_IFR_ONE_OF_OPTION_OP
:
958 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
959 FormTags
->Tags
[QuestionIndex
].Key
= ((EFI_IFR_ONE_OF_OPTION
*) &FormData
[Index
])->Key
;
960 FormTags
->Tags
[QuestionIndex
].ResetRequired
= (BOOLEAN
) (FormTags
->Tags
[QuestionIndex
].Flags
& EFI_IFR_FLAG_RESET_REQUIRED
);
963 case EFI_IFR_CHECKBOX_OP
:
964 GetQuestionHeader (&FormTags
->Tags
[CurrTag
], FormData
, Index
, FileFormTags
, CurrentVariable
);
965 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
968 case EFI_IFR_NUMERIC_OP
:
969 GetNumericHeader (&FormTags
->Tags
[CurrTag
], FormData
, Index
, (UINT16
) 1, FileFormTags
, CurrentVariable
);
970 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
973 case EFI_IFR_DATE_OP
:
975 // Date elements come in as a Year, Month, Day. We need to process them as a country-based
976 // Order. It is much easier to do it here than anywhere else.
978 // For US standards - we want Month/Day/Year, thus we advance "i" +1, +2, +0 while CurrTag is +0, +1, +2
981 &FormTags
->Tags
[CurrTag
],
983 (UINT16
) (Index
+ TagLength
),
990 // The current language selected + the Date operand
992 FormTags
->Tags
[CurrTag
+ 1].Operand
= FormData
[Index
];
994 &FormTags
->Tags
[CurrTag
+ 1],
996 (UINT16
) (Index
+ TagLength
+ FormData
[Index
+ TagLength
+ 1]),
1003 // The current language selected + the Date operand
1005 FormTags
->Tags
[CurrTag
+ 2].Operand
= FormData
[Index
];
1006 GetNumericHeader (&FormTags
->Tags
[CurrTag
+ 2], FormData
, Index
, (UINT16
) 1, FileFormTags
, CurrentVariable
);
1008 CurrTag
= (INT16
) (CurrTag
+ 2);
1010 Index
= (UINT16
) (Index
+ TagLength
);
1014 TagLength
= FormData
[Index
+ 1];
1015 Index
= (UINT16
) (Index
+ TagLength
);
1019 TagLength
= FormData
[Index
+ 1];
1022 case EFI_IFR_TIME_OP
:
1023 GetNumericHeader (&FormTags
->Tags
[CurrTag
], FormData
, Index
, (UINT16
) 0, FileFormTags
, CurrentVariable
);
1027 // Override the GetQuestionHeader information - date/time are treated very differently
1029 FormTags
->Tags
[CurrTag
].NumberOfLines
= 1;
1033 // The premise is that every date/time op-code have 3 elements, the first 2 have 0 lines
1034 // associated with them, and the third has 1 line to allow to space beyond the choice.
1040 case EFI_IFR_PASSWORD_OP
:
1041 case EFI_IFR_STRING_OP
:
1042 GetQuestionHeader (&FormTags
->Tags
[CurrTag
], FormData
, Index
, FileFormTags
, CurrentVariable
);
1043 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
1046 case EFI_IFR_INCONSISTENT_IF_OP
:
1047 case EFI_IFR_SUPPRESS_IF_OP
:
1048 case EFI_IFR_GRAYOUT_IF_OP
:
1052 case EFI_IFR_EQ_ID_VAL_OP
:
1053 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
1054 FormTags
->Tags
[CurrTag
].ConsistencyId
= ConsistencyId
;
1057 case EFI_IFR_EQ_VAR_VAL_OP
:
1058 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
1059 FormTags
->Tags
[CurrTag
].ConsistencyId
= ConsistencyId
;
1062 case EFI_IFR_EQ_ID_ID_OP
:
1063 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
1064 FormTags
->Tags
[CurrTag
].ConsistencyId
= ConsistencyId
;
1067 case EFI_IFR_AND_OP
:
1069 case EFI_IFR_NOT_OP
:
1070 case EFI_IFR_TRUE_OP
:
1071 case EFI_IFR_FALSE_OP
:
1074 FormTags
->Tags
[CurrTag
].ConsistencyId
= ConsistencyId
;
1077 case EFI_IFR_EQ_ID_LIST_OP
:
1078 IfrToFormTag (FormData
[Index
], &FormTags
->Tags
[CurrTag
], (VOID
*) &FormData
[Index
], NULL
);
1080 FormTags
->Tags
[CurrTag
].ConsistencyId
= ConsistencyId
;
1093 // Per spec., we ignore ops that we don't know how to deal with. Skip to next tag
1095 Index
= (UINT16
) (Index
+ TagLength
);
1105 ExtractDynamicFormHandle (
1106 IN UI_MENU_OPTION
*Selection
,
1107 IN UINT8
*CallbackData
,
1108 IN EFI_FILE_FORM_TAGS
*FileFormTagsHead
,
1110 OUT UINT16
*FormHandle
,
1111 OUT UINT16
*TitleToken
,
1112 OUT EFI_FORM_TAGS
*FormTags
1116 Routine Description:
1118 The function does the most of the works when the EFI_TAG that
1119 user selects on is EFI_IFR_FLAG_INTERACTIVE or EFI_IFR_PASSWORD_OP:
1120 invoke CallBack, update the new form data.
1124 Selection - The current selection of the form.
1125 CallbackData - The pointer to host the data passed back by the callback function.
1126 FileFormTagsHead - Prompt string token of the one-of box
1127 IdValue - The current page number.
1128 FormHandle - Output the the handle of the form.
1129 TitleToken - Output the TitleToken of the new page.
1130 FormTags - Output the FormFags of the new page.
1139 EFI_FILE_FORM_TAGS
*FileFormTags
;
1140 EFI_FORM_TAGS
*LocalTags
;
1141 EFI_FORM_CALLBACK_PROTOCOL
*FormCallback
;
1145 EFI_PHYSICAL_ADDRESS CallbackHandle
;
1148 EFI_HII_CALLBACK_PACKET
*Packet
;
1150 CHAR16 NullCharacter
;
1152 UINT16 ConsistencyId
;
1153 UINT16 CurrentVariable
;
1154 EFI_VARIABLE_DEFINITION
*VariableDefinition
;
1155 EFI_IFR_DATA_ENTRY
*DataEntry
;
1157 VariableDefinition
= NULL
;
1158 NullCharacter
= CHAR_NULL
;
1160 CurrentVariable
= 0;
1161 FileFormTags
= FileFormTagsHead
;
1164 TargetPage
= (UINT16
) IdValue
;
1169 // Advance FileFormTags to the correct file's tag information.
1170 // For instance, if Selection->IfrNumber is 3, that means the 4th
1171 // file (0-based) in the FileFormTags linked-list contains the tag
1174 for (Index
= 0; Index
< Selection
->IfrNumber
; Index
++) {
1175 FileFormTags
= FileFormTags
->NextFile
;
1178 LocalTags
= &FileFormTags
->FormTags
;
1181 // Advance Index to the FormOp with the correct ID value
1183 for (; LocalTags
->Next
!= NULL
; LocalTags
= LocalTags
->Next
) {
1184 if ((LocalTags
->Tags
[0].CallbackHandle
!= 0) && (CallbackHandle
== 0)) {
1185 CallbackHandle
= LocalTags
->Tags
[0].CallbackHandle
;
1186 CopyMem (&TagGuid
, &LocalTags
->Tags
[0].GuidValue
, sizeof (EFI_GUID
));
1189 for (Index
= 0; LocalTags
->Tags
[Index
].Operand
!= EFI_IFR_FORM_OP
; Index
++)
1191 if (LocalTags
->Tags
[Index
].Id
== IdValue
) {
1196 // If we are going to callback on a non-goto opcode, make sure we don't change pages
1198 if (Selection
->ThisTag
->Operand
!= EFI_IFR_REF_OP
) {
1199 TargetPage
= Selection
->FormId
;
1202 // The first tag below should be the form op-code. We need to store away the
1203 // current variable setting to ensure if we have to reload the page, that we
1204 // can correctly restore the values for the active variable
1206 CurrentVariable
= Selection
->Tags
[0].VariableNumber
;
1209 // Remember that dynamic pages in an environment where all pages are not
1210 // dynamic require us to call back to the user to give them an opportunity
1211 // to register fresh information in the HII database so that we can extract it.
1213 Status
= gBS
->HandleProtocol (
1214 (VOID
*) (UINTN
) CallbackHandle
,
1215 &gEfiFormCallbackProtocolGuid
,
1216 (VOID
**) &FormCallback
1219 if (EFI_ERROR (Status
)) {
1220 FreePool (LocalTags
->Tags
);
1224 ExtractRequestedNvMap (FileFormTags
, CurrentVariable
, &VariableDefinition
);
1226 if (Selection
->ThisTag
->Flags
& (EFI_IFR_FLAG_INTERACTIVE
| EFI_IFR_FLAG_NV_ACCESS
)) {
1227 ((EFI_IFR_DATA_ARRAY
*) CallbackData
)->NvRamMap
= VariableDefinition
->NvRamMap
;
1229 ((EFI_IFR_DATA_ARRAY
*) CallbackData
)->NvRamMap
= NULL
;
1232 if ((FormCallback
!= NULL
) && (FormCallback
->Callback
!= NULL
)) {
1233 Status
= FormCallback
->Callback (
1235 Selection
->ThisTag
->Key
,
1236 (EFI_IFR_DATA_ARRAY
*) CallbackData
,
1241 if (EFI_ERROR (Status
)) {
1243 // Restore Previous Value
1246 &VariableDefinition
->NvRamMap
[Selection
->ThisTag
->StorageStart
],
1248 Selection
->ThisTag
->StorageWidth
1251 if (Packet
!= NULL
) {
1253 // Upon error, we will likely receive a string to print out
1255 ScreenSize
= GetStringWidth (Packet
->String
) / 2;
1258 // Display error popup
1260 CreatePopUp (ScreenSize
, 3, &NullCharacter
, Packet
->String
, &NullCharacter
);
1263 Status
= WaitForKeyStroke (&Key
);
1264 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
1266 UpdateStatusBar (INPUT_ERROR
, (UINT8
) 0, TRUE
);
1270 if (Packet
!= NULL
) {
1272 // We need to on a non-error, look in the outbound Packet for information and update the NVRAM
1273 // location associated with the op-code specified there. This is used on single op-code instances
1274 // and not for when a hyperlink sent us a whole page of data.
1276 DataEntry
= (EFI_IFR_DATA_ENTRY
*) (&Packet
->DataArray
+ 1);
1277 if (Packet
->DataArray
.EntryCount
== 1) {
1278 switch (DataEntry
->OpCode
) {
1279 case EFI_IFR_STRING_OP
:
1280 case EFI_IFR_NUMERIC_OP
:
1281 case EFI_IFR_ORDERED_LIST_OP
:
1282 case EFI_IFR_ONE_OF_OP
:
1283 case EFI_IFR_CHECKBOX_OP
:
1285 &VariableDefinition
->NvRamMap
[Selection
->ThisTag
->StorageStart
],
1287 Selection
->ThisTag
->StorageWidth
1291 case EFI_IFR_NV_ACCESS_COMMAND
:
1293 &VariableDefinition
->NvRamMap
[((EFI_IFR_NV_DATA
*) Packet
)->QuestionId
],
1294 ((EFI_IFR_NV_DATA
*) Packet
) + 1,
1295 ((EFI_IFR_NV_DATA
*) Packet
)->StorageWidth
1301 if (DataEntry
->Flags
& RESET_REQUIRED
) {
1302 gResetRequired
= TRUE
;
1305 if (DataEntry
->Flags
& EXIT_REQUIRED
) {
1306 gExitRequired
= TRUE
;
1309 if (DataEntry
->Flags
& SAVE_REQUIRED
) {
1310 gSaveRequired
= TRUE
;
1313 if (DataEntry
->Flags
& NV_CHANGED
) {
1314 gNvUpdateRequired
= TRUE
;
1317 if (DataEntry
->Flags
& NV_NOT_CHANGED
) {
1318 gNvUpdateRequired
= FALSE
;
1324 if (Packet
!= NULL
) {
1328 for (BackupIndex
= 0; LocalTags
->Tags
[BackupIndex
].Operand
!= EFI_IFR_END_FORM_OP
; BackupIndex
++) {
1329 switch (LocalTags
->Tags
[BackupIndex
].Operand
) {
1330 case EFI_IFR_EQ_VAR_VAL_OP
:
1331 case EFI_IFR_EQ_ID_VAL_OP
:
1332 case EFI_IFR_EQ_ID_ID_OP
:
1333 case EFI_IFR_AND_OP
:
1335 case EFI_IFR_NOT_OP
:
1336 case EFI_IFR_TRUE_OP
:
1337 case EFI_IFR_FALSE_OP
:
1340 case EFI_IFR_EQ_ID_LIST_OP
:
1342 // If we encountered a ConsistencyId value, on this page they will be incremental
1343 // So register the first value we encounter. We will pass this in when we re-create this page
1345 if ((LocalTags
->Tags
[BackupIndex
].ConsistencyId
!= 0) && (ConsistencyId
== 0)) {
1346 ConsistencyId
= (UINT16
) (LocalTags
->Tags
[BackupIndex
].ConsistencyId
- 1);
1352 // Delete the buffer associated with previous dynamic page
1353 // We will re-allocate a buffer....
1355 FreePool (LocalTags
->Tags
);
1358 Buffer
= AllocateZeroPool (Length
);
1359 ASSERT (Buffer
!= NULL
);
1362 // Get the form that was updated by the callback
1373 // Ok, we have the new page.....now we must purge the old page and re-allocate
1374 // the tag page with the new data
1385 // return the Form Id, Text, and the File's FormTags structure
1387 *FormHandle
= LocalTags
->Tags
[0].Id
;
1388 *TitleToken
= LocalTags
->Tags
[0].Text
;
1389 *FormTags
= *LocalTags
;
1391 FormTags
->Tags
[0].CallbackHandle
= CallbackHandle
;
1392 CopyMem (&FormTags
->Tags
[0].GuidValue
, &TagGuid
, sizeof (EFI_GUID
));
1399 IN UI_MENU_OPTION
*Selection
,
1400 IN BOOLEAN Callback
,
1401 IN EFI_FILE_FORM_TAGS
*FileFormTagsHead
,
1402 IN UINT8
*CallbackData
1407 EFI_FORM_TAGS FormTags
;
1412 // Displays the Header and Footer borders
1414 DisplayPageFrame ();
1417 // Id of 0 yields the getting of the top form whatever the ID is. Usually the first form in the IFR
1419 ExtractFormHandle (Selection
, FileFormTagsHead
, 0, &FormHandle
, &TitleToken
, &FormTags
);
1421 Selection
= DisplayForm (Selection
, FormHandle
, TitleToken
, FormTags
, FileFormTagsHead
, CallbackData
);
1424 // If selection is null use the former selection
1426 if (Selection
== NULL
) {
1434 while (Selection
->Tags
!= NULL
) {
1435 if (Selection
->Previous
) {
1436 ExtractFormHandle (Selection
, FileFormTagsHead
, Selection
->FormId
, &FormHandle
, &TitleToken
, &FormTags
);
1439 // True if a hyperlink/jump is selected
1441 if (Selection
->ThisTag
->Operand
== EFI_IFR_REF_OP
&& Selection
->ThisTag
->Id
!= 0x0000) {
1442 if (Selection
->ThisTag
->Flags
& EFI_IFR_FLAG_INTERACTIVE
) {
1443 ExtractDynamicFormHandle (
1447 Selection
->ThisTag
->Id
,
1454 ExtractFormHandle (Selection
, FileFormTagsHead
, Selection
->ThisTag
->Id
, &FormHandle
, &TitleToken
, &FormTags
);
1459 if ((Selection
->ThisTag
->Flags
& EFI_IFR_FLAG_INTERACTIVE
) &&
1460 (Selection
->ThisTag
->Operand
!= EFI_IFR_PASSWORD_OP
)
1462 ExtractDynamicFormHandle (
1472 ExtractFormHandle (Selection
, FileFormTagsHead
, Selection
->FormId
, &FormHandle
, &TitleToken
, &FormTags
);
1478 // Displays the Header and Footer borders
1480 DisplayPageFrame ();
1482 Selection
= DisplayForm (Selection
, FormHandle
, TitleToken
, FormTags
, FileFormTagsHead
, CallbackData
);
1484 if (Selection
== NULL
) {