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
;
455 return EFI_NOT_FOUND
;
458 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
459 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
461 // This option is suppressed
470 // Current selected option happen to be suppressed,
471 // enforce to select on a non-suppressed option
473 Link
= GetFirstNode (&Question
->OptionListHead
);
474 while (!IsNull (&Question
->OptionListHead
, Link
)) {
475 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
477 if ((OneOfOption
->SuppressExpression
== NULL
) ||
478 !OneOfOption
->SuppressExpression
->Result
.Value
.b
) {
480 CopyMem (QuestionValue
, &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
481 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
482 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
483 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
487 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
492 Character
[0] = LEFT_ONEOF_DELIMITER
;
493 NewStrCat (OptionString
[0], Character
);
494 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
495 NewStrCat (OptionString
[0], StringPtr
);
496 Character
[0] = RIGHT_ONEOF_DELIMITER
;
497 NewStrCat (OptionString
[0], Character
);
499 FreePool (StringPtr
);
504 case EFI_IFR_CHECKBOX_OP
:
505 *OptionString
= AllocateZeroPool (BufferSize
);
506 ASSERT (*OptionString
);
508 *OptionString
[0] = LEFT_CHECKBOX_DELIMITER
;
512 // Since this is a BOOLEAN operation, flip it upon selection
514 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
517 // Perform inconsistent check
519 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
520 if (EFI_ERROR (Status
)) {
522 // Inconsistent check fail, restore Question Value
524 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
525 FreePool (*OptionString
);
526 *OptionString
= NULL
;
531 // Save Question value
533 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
534 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
537 if (QuestionValue
->Value
.b
) {
538 *(OptionString
[0] + 1) = CHECK_ON
;
540 *(OptionString
[0] + 1) = CHECK_OFF
;
542 *(OptionString
[0] + 2) = RIGHT_CHECKBOX_DELIMITER
;
545 case EFI_IFR_NUMERIC_OP
:
550 Status
= GetNumericInput (Selection
, MenuOption
);
552 *OptionString
= AllocateZeroPool (BufferSize
);
553 ASSERT (*OptionString
);
555 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
560 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
561 Number
= (UINT16
) GetStringWidth (FormattedNumber
);
562 CopyMem (OptionString
[0] + 1, FormattedNumber
, Number
);
564 *(OptionString
[0] + Number
/ 2) = RIGHT_NUMERIC_DELIMITER
;
568 case EFI_IFR_DATE_OP
:
571 // This is similar to numerics
573 Status
= GetNumericInput (Selection
, MenuOption
);
575 *OptionString
= AllocateZeroPool (BufferSize
);
576 ASSERT (*OptionString
);
578 switch (MenuOption
->Sequence
) {
580 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
581 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Month
);
582 *(OptionString
[0] + 3) = DATE_SEPARATOR
;
586 SetUnicodeMem (OptionString
[0], 4, L
' ');
587 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Day
);
588 *(OptionString
[0] + 6) = DATE_SEPARATOR
;
592 SetUnicodeMem (OptionString
[0], 7, L
' ');
593 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%4d", QuestionValue
->Value
.date
.Year
);
594 *(OptionString
[0] + 11) = RIGHT_NUMERIC_DELIMITER
;
600 case EFI_IFR_TIME_OP
:
603 // This is similar to numerics
605 Status
= GetNumericInput (Selection
, MenuOption
);
607 *OptionString
= AllocateZeroPool (BufferSize
);
608 ASSERT (*OptionString
);
610 switch (MenuOption
->Sequence
) {
612 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
613 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Hour
);
614 *(OptionString
[0] + 3) = TIME_SEPARATOR
;
618 SetUnicodeMem (OptionString
[0], 4, L
' ');
619 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Minute
);
620 *(OptionString
[0] + 6) = TIME_SEPARATOR
;
624 SetUnicodeMem (OptionString
[0], 7, L
' ');
625 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Second
);
626 *(OptionString
[0] + 9) = RIGHT_NUMERIC_DELIMITER
;
632 case EFI_IFR_STRING_OP
:
634 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
637 Status
= ReadString (MenuOption
, gPromptForData
, StringPtr
);
638 if (!EFI_ERROR (Status
)) {
639 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
640 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
642 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
645 FreePool (StringPtr
);
647 *OptionString
= AllocateZeroPool (BufferSize
);
648 ASSERT (*OptionString
);
650 if (((CHAR16
*) Question
->BufferValue
)[0] == 0x0000) {
651 *(OptionString
[0]) = '_';
653 if ((Maximum
* sizeof (CHAR16
)) < BufferSize
) {
654 BufferSize
= Maximum
* sizeof (CHAR16
);
656 CopyMem (OptionString
[0], (CHAR16
*) Question
->BufferValue
, BufferSize
);
661 case EFI_IFR_PASSWORD_OP
:
663 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
667 // For interactive passwords, old password is validated by callback
669 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
671 // Use a NULL password to test whether old password is required
674 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
675 if (Status
== EFI_NOT_AVAILABLE_YET
) {
677 // Callback request to terminate password input
679 FreePool (StringPtr
);
683 if (EFI_ERROR (Status
)) {
685 // Old password exist, ask user for the old password
687 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
688 if (EFI_ERROR (Status
)) {
689 FreePool (StringPtr
);
694 // Check user input old password
696 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
697 if (EFI_ERROR (Status
)) {
698 if (Status
== EFI_NOT_READY
) {
700 // Typed in old password incorrect
704 Status
= EFI_SUCCESS
;
707 FreePool (StringPtr
);
713 // For non-interactive password, validate old password in local
715 if (*((CHAR16
*) Question
->BufferValue
) != 0) {
717 // There is something there! Prompt for password
719 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
720 if (EFI_ERROR (Status
)) {
721 FreePool (StringPtr
);
725 TempString
= AllocateCopyPool ((Maximum
+ 1) * sizeof (CHAR16
), Question
->BufferValue
);
726 ASSERT (TempString
!= NULL
);
728 TempString
[Maximum
] = L
'\0';
730 if (StrCmp (StringPtr
, TempString
) != 0) {
732 // Typed in old password incorrect
736 FreePool (StringPtr
);
737 FreePool (TempString
);
741 FreePool (TempString
);
746 // Ask for new password
748 ZeroMem (StringPtr
, (Maximum
+ 1) * sizeof (CHAR16
));
749 Status
= ReadString (MenuOption
, gPromptForNewPassword
, StringPtr
);
750 if (EFI_ERROR (Status
)) {
752 // Reset state machine for interactive password
754 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
755 PasswordCallback (Selection
, MenuOption
, NULL
);
758 FreePool (StringPtr
);
763 // Confirm new password
765 TempString
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
767 Status
= ReadString (MenuOption
, gConfirmPassword
, TempString
);
768 if (EFI_ERROR (Status
)) {
770 // Reset state machine for interactive password
772 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
773 PasswordCallback (Selection
, MenuOption
, NULL
);
776 FreePool (StringPtr
);
777 FreePool (TempString
);
782 // Compare two typed-in new passwords
784 if (StrCmp (StringPtr
, TempString
) == 0) {
786 // Two password match, send it to Configuration Driver
788 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
789 PasswordCallback (Selection
, MenuOption
, StringPtr
);
791 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
792 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
796 // Reset state machine for interactive password
798 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
799 PasswordCallback (Selection
, MenuOption
, NULL
);
803 // Two password mismatch, prompt error message
806 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gConfirmError
, gPressEnter
, gEmptyString
);
807 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
810 FreePool (TempString
);
811 FreePool (StringPtr
);
824 Process the help string: Split StringPtr to several lines of strings stored in
825 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
827 @param StringPtr The entire help string.
828 @param FormattedString The oupput formatted string.
829 @param RowCount TRUE: if Question is selected.
834 IN CHAR16
*StringPtr
,
835 OUT CHAR16
**FormattedString
,
842 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
847 UINTN VirtualLineCount
;
849 // GlyphOffset stores glyph width of current screen-line
853 // GlyphWidth equals to 2 if we meet width directive
857 // during scanning, we remember the position of last space character
858 // in case that if next word cannot put in current line, we could restore back to the position
859 // of last space character
860 // while we should also remmeber the glyph width of the last space character for restoring
862 UINTN LastSpaceIndex
;
863 UINTN LastSpaceGlyphWidth
;
865 // every time we begin to form a new screen-line, we should remember glyph width of single character
868 UINTN LineStartGlyphWidth
;
870 UINTN
*OldIndexArray
;
872 BlockWidth
= (UINTN
) gHelpBlockWidth
- 1;
875 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
876 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
877 // to bring the width directive of the last line to current screen-line.
878 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
879 // different from that of "\wideabcde", we should remember the width directive.
882 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
883 ASSERT (IndexArray
!= NULL
);
885 if (*FormattedString
!= NULL
) {
886 FreePool (*FormattedString
);
887 *FormattedString
= NULL
;
890 for (PrevCurrIndex
= 0, CurrIndex
= 0, LineCount
= 0, LastSpaceIndex
= 0,
891 IndexArray
[0] = 0, GlyphWidth
= 1, GlyphOffset
= 0, LastSpaceGlyphWidth
= 1, LineStartGlyphWidth
= 1;
892 (StringPtr
[CurrIndex
] != CHAR_NULL
);
895 if (LineCount
== AllocateSize
) {
896 AllocateSize
+= 0x10;
897 OldIndexArray
= IndexArray
;
898 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
899 ASSERT (IndexArray
!= NULL
);
901 CopyMem (IndexArray
, OldIndexArray
, LineCount
* sizeof (UINTN
) * 3);
902 FreePool (OldIndexArray
);
904 switch (StringPtr
[CurrIndex
]) {
908 GlyphWidth
= ((StringPtr
[CurrIndex
] == WIDE_CHAR
) ? 2 : 1);
909 if (CurrIndex
== 0) {
910 LineStartGlyphWidth
= GlyphWidth
;
916 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
920 // Store a range of string as a line
922 IndexArray
[LineCount
*3] = PrevCurrIndex
;
923 IndexArray
[LineCount
*3+1] = CurrIndex
;
924 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
927 // Reset offset and save begin position of line
930 LineStartGlyphWidth
= GlyphWidth
;
931 PrevCurrIndex
= CurrIndex
+ 1;
936 // "\r\n" and "\r" both are handled here
938 case CHAR_CARRIAGE_RETURN
:
939 if (StringPtr
[CurrIndex
+ 1] == CHAR_LINEFEED
) {
943 IndexArray
[LineCount
*3] = PrevCurrIndex
;
944 IndexArray
[LineCount
*3+1] = CurrIndex
;
945 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
950 LineStartGlyphWidth
= GlyphWidth
;
951 PrevCurrIndex
= CurrIndex
+ 1;
955 // char is space or other char
958 GlyphOffset
+= GlyphWidth
;
959 if (GlyphOffset
>= BlockWidth
) {
960 if (LastSpaceIndex
> PrevCurrIndex
) {
962 // LastSpaceIndex points to space inside current screen-line,
963 // restore to LastSpaceIndex
964 // (Otherwise the word is too long to fit one screen-line, just cut it)
966 CurrIndex
= LastSpaceIndex
;
967 GlyphWidth
= LastSpaceGlyphWidth
;
968 } else if (GlyphOffset
> BlockWidth
) {
970 // the word is too long to fit one screen-line and we don't get the chance
971 // of GlyphOffset == BlockWidth because GlyphWidth = 2
976 IndexArray
[LineCount
*3] = PrevCurrIndex
;
977 IndexArray
[LineCount
*3+1] = CurrIndex
+ 1;
978 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
979 LineStartGlyphWidth
= GlyphWidth
;
982 // Reset offset and save begin position of line
985 PrevCurrIndex
= CurrIndex
+ 1;
989 // LastSpaceIndex: remember position of last space
991 if (StringPtr
[CurrIndex
] == CHAR_SPACE
) {
992 LastSpaceIndex
= CurrIndex
;
993 LastSpaceGlyphWidth
= GlyphWidth
;
999 if (GlyphOffset
> 0) {
1000 IndexArray
[LineCount
*3] = PrevCurrIndex
;
1001 IndexArray
[LineCount
*3+1] = CurrIndex
;
1002 IndexArray
[LineCount
*3+2] = GlyphWidth
;
1006 if (LineCount
== 0) {
1008 // in case we meet null string
1013 // we assume null string's glyph width is 1
1019 VirtualLineCount
= RowCount
* (LineCount
/ RowCount
+ (LineCount
% RowCount
> 0));
1020 *FormattedString
= AllocateZeroPool (VirtualLineCount
* (BlockWidth
+ 1) * sizeof (CHAR16
) * 2);
1021 ASSERT (*FormattedString
!= NULL
);
1023 for (CurrIndex
= 0; CurrIndex
< LineCount
; CurrIndex
++) {
1024 *(*FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1)) = (CHAR16
) ((IndexArray
[CurrIndex
*3+2] == 2) ? WIDE_CHAR
: NARROW_CHAR
);
1026 *FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1) + 1,
1027 StringPtr
+ IndexArray
[CurrIndex
*3],
1028 IndexArray
[CurrIndex
*3+1]-IndexArray
[CurrIndex
*3]
1032 FreePool (IndexArray
);