2 Implementation for handling the User Interface option processing.
5 Copyright (c) 2004 - 2008, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 Process Question Config.
23 @param Selection The UI menu selection.
24 @param Question The Question to be peocessed.
26 @retval EFI_SUCCESS Question Config process success.
27 @retval Other Question Config process fail.
31 ProcessQuestionConfig (
32 IN UI_MENU_SELECTION
*Selection
,
33 IN FORM_BROWSER_STATEMENT
*Question
39 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
41 if (Question
->QuestionConfig
== 0) {
48 ConfigResp
= GetToken (Question
->QuestionConfig
, Selection
->FormSet
->HiiHandle
);
49 if (ConfigResp
== NULL
) {
54 // Send config to Configuration Driver
56 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
57 if (ConfigAccess
== NULL
) {
58 return EFI_UNSUPPORTED
;
60 Status
= ConfigAccess
->RouteConfig (
71 Search an Option of a Question by its value.
73 @param Question The Question
74 @param OptionValue Value for Option to be searched.
76 @retval Pointer Pointer to the found Option.
77 @retval NULL Option not found.
82 IN FORM_BROWSER_STATEMENT
*Question
,
83 IN EFI_HII_VALUE
*OptionValue
87 QUESTION_OPTION
*Option
;
89 Link
= GetFirstNode (&Question
->OptionListHead
);
90 while (!IsNull (&Question
->OptionListHead
, Link
)) {
91 Option
= QUESTION_OPTION_FROM_LINK (Link
);
93 if (CompareHiiValue (&Option
->Value
, OptionValue
, NULL
) == 0) {
97 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
105 Print Question Value according to it's storage width and display attributes.
107 @param Question The Question to be printed.
108 @param FormattedNumber Buffer for output string.
109 @param BufferSize The FormattedNumber buffer size in bytes.
111 @retval EFI_SUCCESS Print success.
112 @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
116 PrintFormattedNumber (
117 IN FORM_BROWSER_STATEMENT
*Question
,
118 IN OUT CHAR16
*FormattedNumber
,
124 EFI_HII_VALUE
*QuestionValue
;
126 if (BufferSize
< (21 * sizeof (CHAR16
))) {
127 return EFI_BUFFER_TOO_SMALL
;
130 QuestionValue
= &Question
->HiiValue
;
132 Value
= (INT64
) QuestionValue
->Value
.u64
;
133 switch (Question
->Flags
& EFI_IFR_DISPLAY
) {
134 case EFI_IFR_DISPLAY_INT_DEC
:
135 switch (QuestionValue
->Type
) {
136 case EFI_IFR_NUMERIC_SIZE_1
:
137 Value
= (INT64
) ((INT8
) QuestionValue
->Value
.u8
);
140 case EFI_IFR_NUMERIC_SIZE_2
:
141 Value
= (INT64
) ((INT16
) QuestionValue
->Value
.u16
);
144 case EFI_IFR_NUMERIC_SIZE_4
:
145 Value
= (INT64
) ((INT32
) QuestionValue
->Value
.u32
);
148 case EFI_IFR_NUMERIC_SIZE_8
:
161 case EFI_IFR_DISPLAY_UINT_DEC
:
165 case EFI_IFR_DISPLAY_UINT_HEX
:
170 return EFI_UNSUPPORTED
;
174 UnicodeSPrint (FormattedNumber
, BufferSize
, Format
, Value
);
181 Password may be stored as encrypted by Configuration Driver. When change a
182 password, user will be challenged with old password. To validate user input old
183 password, we will send the clear text to Configuration Driver via Callback().
184 Configuration driver is responsible to check the passed in password and return
185 the validation result. If validation pass, state machine in password Callback()
186 will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.
187 After user type in new password twice, Callback() will be invoked to send the
188 new password to Configuration Driver.
190 @param Selection Pointer to UI_MENU_SELECTION.
191 @param MenuOption The MenuOption for this password Question.
192 @param String The clear text of password.
194 @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input.
195 @return In state of BROWSER_STATE_VALIDATE_PASSWORD:
196 @retval EFI_SUCCESS Password correct, Browser will prompt for new
198 @retval EFI_NOT_READY Password incorrect, Browser will show error
200 @retval Other Browser will do nothing.
201 @return In state of BROWSER_STATE_SET_PASSWORD:
202 @retval EFI_SUCCESS Set password success.
203 @retval Other Set password failed.
208 IN UI_MENU_SELECTION
*Selection
,
209 IN UI_MENU_OPTION
*MenuOption
,
214 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
215 EFI_BROWSER_ACTION_REQUEST ActionRequest
;
216 EFI_HII_VALUE
*QuestionValue
;
218 QuestionValue
= &MenuOption
->ThisTag
->HiiValue
;
219 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
220 if (ConfigAccess
== NULL
) {
221 return EFI_UNSUPPORTED
;
225 // Prepare password string in HII database
227 if (String
!= NULL
) {
228 QuestionValue
->Value
.string
= NewString (String
, Selection
->FormSet
->HiiHandle
);
230 QuestionValue
->Value
.string
= 0;
234 // Send password to Configuration Driver for validation
236 Status
= ConfigAccess
->Callback (
238 EFI_BROWSER_ACTION_CHANGING
,
239 MenuOption
->ThisTag
->QuestionId
,
241 &QuestionValue
->Value
,
246 // Remove password string from HII database
248 if (String
!= NULL
) {
249 DeleteString (QuestionValue
->Value
.string
, Selection
->FormSet
->HiiHandle
);
257 Display error message for invalid password.
268 // Invalid password, prompt error message
271 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gPassowordInvalid
, gPressEnter
, gEmptyString
);
272 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
277 Process a Question's Option (whether selected or un-selected).
279 @param Selection Pointer to UI_MENU_SELECTION.
280 @param MenuOption The MenuOption for this Question.
281 @param Selected TRUE: if Question is selected.
282 @param OptionString Pointer of the Option String to be displayed.
284 @retval EFI_SUCCESS Question Option process success.
285 @retval Other Question Option process fail.
290 IN UI_MENU_SELECTION
*Selection
,
291 IN UI_MENU_OPTION
*MenuOption
,
293 OUT CHAR16
**OptionString
300 FORM_BROWSER_STATEMENT
*Question
;
301 CHAR16 FormattedNumber
[21];
306 QUESTION_OPTION
*OneOfOption
;
308 EFI_HII_VALUE HiiValue
;
309 EFI_HII_VALUE
*QuestionValue
;
312 QUESTION_OPTION
*Option
;
315 Status
= EFI_SUCCESS
;
318 Character
[1] = L
'\0';
319 *OptionString
= NULL
;
321 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
322 BufferSize
= (gOptionBlockWidth
+ 1) * 2 * gScreenDimensions
.BottomRow
;
324 Question
= MenuOption
->ThisTag
;
325 QuestionValue
= &Question
->HiiValue
;
326 Maximum
= (UINT16
) Question
->Maximum
;
328 switch (Question
->Operand
) {
329 case EFI_IFR_ORDERED_LIST_OP
:
331 // Initialize Option value array
334 if (Question
->BufferValue
[0] == 0) {
335 GetQuestionDefault (Selection
->FormSet
, Selection
->Form
, Question
, 0);
342 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
345 // We now know how many strings we will have, so we can allocate the
346 // space required for the array or strings.
348 *OptionString
= AllocateZeroPool (Question
->MaxContainers
* BufferSize
);
349 ASSERT (*OptionString
);
351 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
352 HiiValue
.Value
.u64
= 0;
353 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
354 HiiValue
.Value
.u8
= Question
->BufferValue
[Index
];
355 if (HiiValue
.Value
.u8
== 0) {
357 // Values for the options in ordered lists should never be a 0
362 OneOfOption
= ValueToOption (Question
, &HiiValue
);
363 if (OneOfOption
== NULL
) {
365 // Show error message
368 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gOptionMismatch
, gPressEnter
, gEmptyString
);
369 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
372 // The initial value of the orderedlist is invalid, force to be valid value
374 Link
= GetFirstNode (&Question
->OptionListHead
);
376 while (!IsNull (&Question
->OptionListHead
, Link
) && Index2
< Question
->MaxContainers
) {
377 Option
= QUESTION_OPTION_FROM_LINK (Link
);
378 Question
->BufferValue
[Index2
++] = Option
->Value
.Value
.u8
;
379 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
381 Question
->BufferValue
[Index2
] = 0;
383 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
384 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
386 FreePool (*OptionString
);
387 *OptionString
= NULL
;
388 return EFI_NOT_FOUND
;
392 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
393 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
395 // This option is suppressed
401 Character
[0] = LEFT_ONEOF_DELIMITER
;
402 NewStrCat (OptionString
[0], Character
);
403 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
404 NewStrCat (OptionString
[0], StringPtr
);
405 Character
[0] = RIGHT_ONEOF_DELIMITER
;
406 NewStrCat (OptionString
[0], Character
);
407 Character
[0] = CHAR_CARRIAGE_RETURN
;
408 NewStrCat (OptionString
[0], Character
);
410 FreePool (StringPtr
);
416 case EFI_IFR_ONE_OF_OP
:
421 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
423 *OptionString
= AllocateZeroPool (BufferSize
);
424 ASSERT (*OptionString
);
426 OneOfOption
= ValueToOption (Question
, QuestionValue
);
427 if (OneOfOption
== NULL
) {
429 // Show error message
432 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gOptionMismatch
, gPressEnter
, gEmptyString
);
433 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
436 // Force the Question value to be valid
438 Link
= GetFirstNode (&Question
->OptionListHead
);
439 while (!IsNull (&Question
->OptionListHead
, Link
)) {
440 Option
= QUESTION_OPTION_FROM_LINK (Link
);
442 if ((Option
->SuppressExpression
== NULL
) ||
443 !Option
->SuppressExpression
->Result
.Value
.b
) {
444 CopyMem (QuestionValue
, &Option
->Value
, sizeof (EFI_HII_VALUE
));
445 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
446 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
450 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
453 FreePool (*OptionString
);
454 *OptionString
= NULL
;
457 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
458 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
460 // This option is suppressed
469 // Current selected option happen to be suppressed,
470 // enforce to select on a non-suppressed option
472 Link
= GetFirstNode (&Question
->OptionListHead
);
473 while (!IsNull (&Question
->OptionListHead
, Link
)) {
474 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
476 if ((OneOfOption
->SuppressExpression
== NULL
) ||
477 !OneOfOption
->SuppressExpression
->Result
.Value
.b
) {
479 CopyMem (QuestionValue
, &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
480 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
481 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
482 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
486 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
491 Character
[0] = LEFT_ONEOF_DELIMITER
;
492 NewStrCat (OptionString
[0], Character
);
493 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
494 NewStrCat (OptionString
[0], StringPtr
);
495 Character
[0] = RIGHT_ONEOF_DELIMITER
;
496 NewStrCat (OptionString
[0], Character
);
498 FreePool (StringPtr
);
503 case EFI_IFR_CHECKBOX_OP
:
504 *OptionString
= AllocateZeroPool (BufferSize
);
505 ASSERT (*OptionString
);
507 *OptionString
[0] = LEFT_CHECKBOX_DELIMITER
;
511 // Since this is a BOOLEAN operation, flip it upon selection
513 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
516 // Perform inconsistent check
518 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
519 if (EFI_ERROR (Status
)) {
521 // Inconsistent check fail, restore Question Value
523 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
524 FreePool (*OptionString
);
525 *OptionString
= NULL
;
530 // Save Question value
532 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
533 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
536 if (QuestionValue
->Value
.b
) {
537 *(OptionString
[0] + 1) = CHECK_ON
;
539 *(OptionString
[0] + 1) = CHECK_OFF
;
541 *(OptionString
[0] + 2) = RIGHT_CHECKBOX_DELIMITER
;
544 case EFI_IFR_NUMERIC_OP
:
549 Status
= GetNumericInput (Selection
, MenuOption
);
551 *OptionString
= AllocateZeroPool (BufferSize
);
552 ASSERT (*OptionString
);
554 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
559 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
560 Number
= (UINT16
) GetStringWidth (FormattedNumber
);
561 CopyMem (OptionString
[0] + 1, FormattedNumber
, Number
);
563 *(OptionString
[0] + Number
/ 2) = RIGHT_NUMERIC_DELIMITER
;
567 case EFI_IFR_DATE_OP
:
570 // This is similar to numerics
572 Status
= GetNumericInput (Selection
, MenuOption
);
574 *OptionString
= AllocateZeroPool (BufferSize
);
575 ASSERT (*OptionString
);
577 switch (MenuOption
->Sequence
) {
579 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
580 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Month
);
581 *(OptionString
[0] + 3) = DATE_SEPARATOR
;
585 SetUnicodeMem (OptionString
[0], 4, L
' ');
586 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Day
);
587 *(OptionString
[0] + 6) = DATE_SEPARATOR
;
591 SetUnicodeMem (OptionString
[0], 7, L
' ');
592 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%4d", QuestionValue
->Value
.date
.Year
);
593 *(OptionString
[0] + 11) = RIGHT_NUMERIC_DELIMITER
;
599 case EFI_IFR_TIME_OP
:
602 // This is similar to numerics
604 Status
= GetNumericInput (Selection
, MenuOption
);
606 *OptionString
= AllocateZeroPool (BufferSize
);
607 ASSERT (*OptionString
);
609 switch (MenuOption
->Sequence
) {
611 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
612 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Hour
);
613 *(OptionString
[0] + 3) = TIME_SEPARATOR
;
617 SetUnicodeMem (OptionString
[0], 4, L
' ');
618 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Minute
);
619 *(OptionString
[0] + 6) = TIME_SEPARATOR
;
623 SetUnicodeMem (OptionString
[0], 7, L
' ');
624 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Second
);
625 *(OptionString
[0] + 9) = RIGHT_NUMERIC_DELIMITER
;
631 case EFI_IFR_STRING_OP
:
633 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
636 Status
= ReadString (MenuOption
, gPromptForData
, StringPtr
);
637 if (!EFI_ERROR (Status
)) {
638 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
639 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
641 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
644 FreePool (StringPtr
);
646 *OptionString
= AllocateZeroPool (BufferSize
);
647 ASSERT (*OptionString
);
649 if (((CHAR16
*) Question
->BufferValue
)[0] == 0x0000) {
650 *(OptionString
[0]) = '_';
652 if ((Maximum
* sizeof (CHAR16
)) < BufferSize
) {
653 BufferSize
= Maximum
* sizeof (CHAR16
);
655 CopyMem (OptionString
[0], (CHAR16
*) Question
->BufferValue
, BufferSize
);
660 case EFI_IFR_PASSWORD_OP
:
662 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
666 // For interactive passwords, old password is validated by callback
668 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
670 // Use a NULL password to test whether old password is required
673 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
674 if (Status
== EFI_NOT_AVAILABLE_YET
) {
676 // Callback request to terminate password input
678 FreePool (StringPtr
);
682 if (EFI_ERROR (Status
)) {
684 // Old password exist, ask user for the old password
686 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
687 if (EFI_ERROR (Status
)) {
688 FreePool (StringPtr
);
693 // Check user input old password
695 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
696 if (EFI_ERROR (Status
)) {
697 if (Status
== EFI_NOT_READY
) {
699 // Typed in old password incorrect
703 Status
= EFI_SUCCESS
;
706 FreePool (StringPtr
);
712 // For non-interactive password, validate old password in local
714 if (*((CHAR16
*) Question
->BufferValue
) != 0) {
716 // There is something there! Prompt for password
718 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
719 if (EFI_ERROR (Status
)) {
720 FreePool (StringPtr
);
724 TempString
= AllocateCopyPool ((Maximum
+ 1) * sizeof (CHAR16
), Question
->BufferValue
);
725 TempString
[Maximum
] = L
'\0';
727 if (StrCmp (StringPtr
, TempString
) != 0) {
729 // Typed in old password incorrect
733 FreePool (StringPtr
);
734 FreePool (TempString
);
738 FreePool (TempString
);
743 // Ask for new password
745 ZeroMem (StringPtr
, (Maximum
+ 1) * sizeof (CHAR16
));
746 Status
= ReadString (MenuOption
, gPromptForNewPassword
, StringPtr
);
747 if (EFI_ERROR (Status
)) {
749 // Reset state machine for interactive password
751 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
752 PasswordCallback (Selection
, MenuOption
, NULL
);
755 FreePool (StringPtr
);
760 // Confirm new password
762 TempString
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
764 Status
= ReadString (MenuOption
, gConfirmPassword
, TempString
);
765 if (EFI_ERROR (Status
)) {
767 // Reset state machine for interactive password
769 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
770 PasswordCallback (Selection
, MenuOption
, NULL
);
773 FreePool (StringPtr
);
774 FreePool (TempString
);
779 // Compare two typed-in new passwords
781 if (StrCmp (StringPtr
, TempString
) == 0) {
783 // Two password match, send it to Configuration Driver
785 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
786 PasswordCallback (Selection
, MenuOption
, StringPtr
);
788 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
789 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
793 // Reset state machine for interactive password
795 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
796 PasswordCallback (Selection
, MenuOption
, NULL
);
800 // Two password mismatch, prompt error message
803 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gConfirmError
, gPressEnter
, gEmptyString
);
804 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
807 FreePool (TempString
);
808 FreePool (StringPtr
);
821 Process the help string: Split StringPtr to several lines of strings stored in
822 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
824 @param StringPtr The entire help string.
825 @param FormattedString The oupput formatted string.
826 @param RowCount TRUE: if Question is selected.
831 IN CHAR16
*StringPtr
,
832 OUT CHAR16
**FormattedString
,
839 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
844 UINTN VirtualLineCount
;
846 // GlyphOffset stores glyph width of current screen-line
850 // GlyphWidth equals to 2 if we meet width directive
854 // during scanning, we remember the position of last space character
855 // in case that if next word cannot put in current line, we could restore back to the position
856 // of last space character
857 // while we should also remmeber the glyph width of the last space character for restoring
859 UINTN LastSpaceIndex
;
860 UINTN LastSpaceGlyphWidth
;
862 // every time we begin to form a new screen-line, we should remember glyph width of single character
865 UINTN LineStartGlyphWidth
;
867 UINTN
*OldIndexArray
;
869 BlockWidth
= (UINTN
) gHelpBlockWidth
- 1;
872 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
873 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
874 // to bring the width directive of the last line to current screen-line.
875 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
876 // different from that of "\wideabcde", we should remember the width directive.
879 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
881 if (*FormattedString
!= NULL
) {
882 FreePool (*FormattedString
);
883 *FormattedString
= NULL
;
886 for (PrevCurrIndex
= 0, CurrIndex
= 0, LineCount
= 0, LastSpaceIndex
= 0,
887 IndexArray
[0] = 0, GlyphWidth
= 1, GlyphOffset
= 0, LastSpaceGlyphWidth
= 1, LineStartGlyphWidth
= 1;
888 (StringPtr
[CurrIndex
] != CHAR_NULL
);
891 if (LineCount
== AllocateSize
) {
892 AllocateSize
+= 0x10;
893 OldIndexArray
= IndexArray
;
894 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
895 CopyMem (IndexArray
, OldIndexArray
, LineCount
* sizeof (UINTN
) * 3);
896 FreePool (OldIndexArray
);
898 switch (StringPtr
[CurrIndex
]) {
902 GlyphWidth
= ((StringPtr
[CurrIndex
] == WIDE_CHAR
) ? 2 : 1);
903 if (CurrIndex
== 0) {
904 LineStartGlyphWidth
= GlyphWidth
;
910 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
914 // Store a range of string as a line
916 IndexArray
[LineCount
*3] = PrevCurrIndex
;
917 IndexArray
[LineCount
*3+1] = CurrIndex
;
918 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
921 // Reset offset and save begin position of line
924 LineStartGlyphWidth
= GlyphWidth
;
925 PrevCurrIndex
= CurrIndex
+ 1;
930 // "\r\n" and "\r" both are handled here
932 case CHAR_CARRIAGE_RETURN
:
933 if (StringPtr
[CurrIndex
+ 1] == CHAR_LINEFEED
) {
937 IndexArray
[LineCount
*3] = PrevCurrIndex
;
938 IndexArray
[LineCount
*3+1] = CurrIndex
;
939 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
944 LineStartGlyphWidth
= GlyphWidth
;
945 PrevCurrIndex
= CurrIndex
+ 1;
949 // char is space or other char
952 GlyphOffset
+= GlyphWidth
;
953 if (GlyphOffset
>= BlockWidth
) {
954 if (LastSpaceIndex
> PrevCurrIndex
) {
956 // LastSpaceIndex points to space inside current screen-line,
957 // restore to LastSpaceIndex
958 // (Otherwise the word is too long to fit one screen-line, just cut it)
960 CurrIndex
= LastSpaceIndex
;
961 GlyphWidth
= LastSpaceGlyphWidth
;
962 } else if (GlyphOffset
> BlockWidth
) {
964 // the word is too long to fit one screen-line and we don't get the chance
965 // of GlyphOffset == BlockWidth because GlyphWidth = 2
970 IndexArray
[LineCount
*3] = PrevCurrIndex
;
971 IndexArray
[LineCount
*3+1] = CurrIndex
+ 1;
972 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
973 LineStartGlyphWidth
= GlyphWidth
;
976 // Reset offset and save begin position of line
979 PrevCurrIndex
= CurrIndex
+ 1;
983 // LastSpaceIndex: remember position of last space
985 if (StringPtr
[CurrIndex
] == CHAR_SPACE
) {
986 LastSpaceIndex
= CurrIndex
;
987 LastSpaceGlyphWidth
= GlyphWidth
;
993 if (GlyphOffset
> 0) {
994 IndexArray
[LineCount
*3] = PrevCurrIndex
;
995 IndexArray
[LineCount
*3+1] = CurrIndex
;
996 IndexArray
[LineCount
*3+2] = GlyphWidth
;
1000 if (LineCount
== 0) {
1002 // in case we meet null string
1007 // we assume null string's glyph width is 1
1013 VirtualLineCount
= RowCount
* (LineCount
/ RowCount
+ (LineCount
% RowCount
> 0));
1014 *FormattedString
= AllocateZeroPool (VirtualLineCount
* (BlockWidth
+ 1) * sizeof (CHAR16
) * 2);
1016 for (CurrIndex
= 0; CurrIndex
< LineCount
; CurrIndex
++) {
1017 *(*FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1)) = (CHAR16
) ((IndexArray
[CurrIndex
*3+2] == 2) ? WIDE_CHAR
: NARROW_CHAR
);
1019 *FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1) + 1,
1020 StringPtr
+ IndexArray
[CurrIndex
*3],
1021 IndexArray
[CurrIndex
*3+1]-IndexArray
[CurrIndex
*3]
1025 FreePool (IndexArray
);