2 Implementation for handling the User Interface option processing.
5 Copyright (c) 2004 - 2007, 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
;
313 Status
= EFI_SUCCESS
;
316 Character
[1] = L
'\0';
317 *OptionString
= NULL
;
319 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
320 BufferSize
= (gOptionBlockWidth
+ 1) * 2 * gScreenDimensions
.BottomRow
;
322 Question
= MenuOption
->ThisTag
;
323 QuestionValue
= &Question
->HiiValue
;
324 Maximum
= (UINT16
) Question
->Maximum
;
326 switch (Question
->Operand
) {
327 case EFI_IFR_ORDERED_LIST_OP
:
329 // Initialize Option value array
331 if (Question
->BufferValue
[0] == 0) {
332 GetQuestionDefault (Selection
->FormSet
, Selection
->Form
, Question
, 0);
339 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
342 // We now know how many strings we will have, so we can allocate the
343 // space required for the array or strings.
345 *OptionString
= AllocateZeroPool (Question
->MaxContainers
* BufferSize
);
346 ASSERT (*OptionString
);
348 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
349 HiiValue
.Value
.u64
= 0;
350 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
351 HiiValue
.Value
.u8
= Question
->BufferValue
[Index
];
352 if (HiiValue
.Value
.u8
== 0) {
354 // Values for the options in ordered lists should never be a 0
359 OneOfOption
= ValueToOption (Question
, &HiiValue
);
360 if (OneOfOption
== NULL
) {
361 gBS
->FreePool (*OptionString
);
362 return EFI_NOT_FOUND
;
366 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
367 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
369 // This option is suppressed
375 Character
[0] = LEFT_ONEOF_DELIMITER
;
376 NewStrCat (OptionString
[0], Character
);
377 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
378 NewStrCat (OptionString
[0], StringPtr
);
379 Character
[0] = RIGHT_ONEOF_DELIMITER
;
380 NewStrCat (OptionString
[0], Character
);
381 Character
[0] = CHAR_CARRIAGE_RETURN
;
382 NewStrCat (OptionString
[0], Character
);
384 gBS
->FreePool (StringPtr
);
390 case EFI_IFR_ONE_OF_OP
:
395 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
397 *OptionString
= AllocateZeroPool (BufferSize
);
398 ASSERT (*OptionString
);
400 OneOfOption
= ValueToOption (Question
, QuestionValue
);
401 if (OneOfOption
== NULL
) {
402 gBS
->FreePool (*OptionString
);
403 return EFI_NOT_FOUND
;
406 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
407 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
409 // This option is suppressed
418 // Current selected option happen to be suppressed,
419 // enforce to select on a non-suppressed option
421 Link
= GetFirstNode (&Question
->OptionListHead
);
422 while (!IsNull (&Question
->OptionListHead
, Link
)) {
423 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
425 if ((OneOfOption
->SuppressExpression
== NULL
) ||
426 !OneOfOption
->SuppressExpression
->Result
.Value
.b
) {
428 CopyMem (QuestionValue
, &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
429 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
433 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
438 Character
[0] = LEFT_ONEOF_DELIMITER
;
439 NewStrCat (OptionString
[0], Character
);
440 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
441 NewStrCat (OptionString
[0], StringPtr
);
442 Character
[0] = RIGHT_ONEOF_DELIMITER
;
443 NewStrCat (OptionString
[0], Character
);
445 gBS
->FreePool (StringPtr
);
450 case EFI_IFR_CHECKBOX_OP
:
451 *OptionString
= AllocateZeroPool (BufferSize
);
452 ASSERT (*OptionString
);
454 *OptionString
[0] = LEFT_CHECKBOX_DELIMITER
;
458 // Since this is a BOOLEAN operation, flip it upon selection
460 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
463 // Perform inconsistent check
465 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
466 if (EFI_ERROR (Status
)) {
468 // Inconsistent check fail, restore Question Value
470 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
471 gBS
->FreePool (*OptionString
);
476 // Save Question value
478 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
479 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
482 if (QuestionValue
->Value
.b
) {
483 *(OptionString
[0] + 1) = CHECK_ON
;
485 *(OptionString
[0] + 1) = CHECK_OFF
;
487 *(OptionString
[0] + 2) = RIGHT_CHECKBOX_DELIMITER
;
490 case EFI_IFR_NUMERIC_OP
:
495 Status
= GetNumericInput (Selection
, MenuOption
);
497 *OptionString
= AllocateZeroPool (BufferSize
);
498 ASSERT (*OptionString
);
500 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
505 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
506 Number
= (UINT16
) GetStringWidth (FormattedNumber
);
507 CopyMem (OptionString
[0] + 1, FormattedNumber
, Number
);
509 *(OptionString
[0] + Number
/ 2) = RIGHT_NUMERIC_DELIMITER
;
513 case EFI_IFR_DATE_OP
:
516 // This is similar to numerics
518 Status
= GetNumericInput (Selection
, MenuOption
);
520 *OptionString
= AllocateZeroPool (BufferSize
);
521 ASSERT (*OptionString
);
523 switch (MenuOption
->Sequence
) {
525 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
526 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Month
);
527 *(OptionString
[0] + 3) = DATE_SEPARATOR
;
531 SetUnicodeMem (OptionString
[0], 4, L
' ');
532 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Day
);
533 *(OptionString
[0] + 6) = DATE_SEPARATOR
;
537 SetUnicodeMem (OptionString
[0], 7, L
' ');
538 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%4d", QuestionValue
->Value
.date
.Year
);
539 *(OptionString
[0] + 11) = RIGHT_NUMERIC_DELIMITER
;
545 case EFI_IFR_TIME_OP
:
548 // This is similar to numerics
550 Status
= GetNumericInput (Selection
, MenuOption
);
552 *OptionString
= AllocateZeroPool (BufferSize
);
553 ASSERT (*OptionString
);
555 switch (MenuOption
->Sequence
) {
557 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
558 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Hour
);
559 *(OptionString
[0] + 3) = TIME_SEPARATOR
;
563 SetUnicodeMem (OptionString
[0], 4, L
' ');
564 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Minute
);
565 *(OptionString
[0] + 6) = TIME_SEPARATOR
;
569 SetUnicodeMem (OptionString
[0], 7, L
' ');
570 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Second
);
571 *(OptionString
[0] + 9) = RIGHT_NUMERIC_DELIMITER
;
577 case EFI_IFR_STRING_OP
:
579 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
582 Status
= ReadString (MenuOption
, gPromptForData
, StringPtr
);
583 if (!EFI_ERROR (Status
)) {
584 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
585 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
587 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
590 gBS
->FreePool (StringPtr
);
592 *OptionString
= AllocateZeroPool (BufferSize
);
593 ASSERT (*OptionString
);
595 if (((CHAR16
*) Question
->BufferValue
)[0] == 0x0000) {
596 *(OptionString
[0]) = '_';
598 if ((Maximum
* sizeof (CHAR16
)) < BufferSize
) {
599 BufferSize
= Maximum
* sizeof (CHAR16
);
601 CopyMem (OptionString
[0], (CHAR16
*) Question
->BufferValue
, BufferSize
);
606 case EFI_IFR_PASSWORD_OP
:
608 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
612 // For interactive passwords, old password is validated by callback
614 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
616 // Use a NULL password to test whether old password is required
619 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
620 if (Status
== EFI_NOT_AVAILABLE_YET
) {
622 // Callback request to terminate password input
624 gBS
->FreePool (StringPtr
);
628 if (EFI_ERROR (Status
)) {
630 // Old password exist, ask user for the old password
632 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
633 if (EFI_ERROR (Status
)) {
634 gBS
->FreePool (StringPtr
);
639 // Check user input old password
641 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
642 if (EFI_ERROR (Status
)) {
643 if (Status
== EFI_NOT_READY
) {
645 // Typed in old password incorrect
649 Status
= EFI_SUCCESS
;
652 gBS
->FreePool (StringPtr
);
658 // For non-interactive password, validate old password in local
660 if (*((CHAR16
*) Question
->BufferValue
) != 0) {
662 // There is something there! Prompt for password
664 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
665 if (EFI_ERROR (Status
)) {
666 gBS
->FreePool (StringPtr
);
670 TempString
= AllocateCopyPool ((Maximum
+ 1) * sizeof (CHAR16
), Question
->BufferValue
);
671 TempString
[Maximum
] = L
'\0';
673 if (StrCmp (StringPtr
, TempString
) != 0) {
675 // Typed in old password incorrect
679 gBS
->FreePool (StringPtr
);
680 gBS
->FreePool (TempString
);
684 gBS
->FreePool (TempString
);
689 // Ask for new password
691 ZeroMem (StringPtr
, (Maximum
+ 1) * sizeof (CHAR16
));
692 Status
= ReadString (MenuOption
, gPromptForNewPassword
, StringPtr
);
693 if (EFI_ERROR (Status
)) {
695 // Reset state machine for interactive password
697 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
698 PasswordCallback (Selection
, MenuOption
, NULL
);
701 gBS
->FreePool (StringPtr
);
706 // Confirm new password
708 TempString
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
710 Status
= ReadString (MenuOption
, gConfirmPassword
, TempString
);
711 if (EFI_ERROR (Status
)) {
713 // Reset state machine for interactive password
715 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
716 PasswordCallback (Selection
, MenuOption
, NULL
);
719 gBS
->FreePool (StringPtr
);
720 gBS
->FreePool (TempString
);
725 // Compare two typed-in new passwords
727 if (StrCmp (StringPtr
, TempString
) == 0) {
729 // Two password match, send it to Configuration Driver
731 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
732 PasswordCallback (Selection
, MenuOption
, StringPtr
);
734 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
735 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
739 // Reset state machine for interactive password
741 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
742 PasswordCallback (Selection
, MenuOption
, NULL
);
746 // Two password mismatch, prompt error message
749 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gConfirmError
, gPressEnter
, gEmptyString
);
750 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
753 gBS
->FreePool (TempString
);
754 gBS
->FreePool (StringPtr
);
767 Process the help string: Split StringPtr to several lines of strings stored in
768 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
770 @param StringPtr The entire help string.
771 @param FormattedString The oupput formatted string.
772 @param RowCount TRUE: if Question is selected.
777 IN CHAR16
*StringPtr
,
778 OUT CHAR16
**FormattedString
,
782 CONST UINTN BlockWidth
= (UINTN
) gHelpBlockWidth
- 1;
785 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
790 UINTN VirtualLineCount
;
792 // GlyphOffset stores glyph width of current screen-line
796 // GlyphWidth equals to 2 if we meet width directive
800 // during scanning, we remember the position of last space character
801 // in case that if next word cannot put in current line, we could restore back to the position
802 // of last space character
803 // while we should also remmeber the glyph width of the last space character for restoring
805 UINTN LastSpaceIndex
;
806 UINTN LastSpaceGlyphWidth
;
808 // every time we begin to form a new screen-line, we should remember glyph width of single character
811 UINTN LineStartGlyphWidth
;
813 UINTN
*OldIndexArray
;
816 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
817 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
818 // to bring the width directive of the last line to current screen-line.
819 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
820 // different from that of "\wideabcde", we should remember the width directive.
823 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
825 if (*FormattedString
!= NULL
) {
826 gBS
->FreePool (*FormattedString
);
827 *FormattedString
= NULL
;
830 for (PrevCurrIndex
= 0, CurrIndex
= 0, LineCount
= 0, LastSpaceIndex
= 0,
831 IndexArray
[0] = 0, GlyphWidth
= 1, GlyphOffset
= 0, LastSpaceGlyphWidth
= 1, LineStartGlyphWidth
= 1;
832 (StringPtr
[CurrIndex
] != CHAR_NULL
);
835 if (LineCount
== AllocateSize
) {
836 AllocateSize
+= 0x10;
837 OldIndexArray
= IndexArray
;
838 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
839 CopyMem (IndexArray
, OldIndexArray
, LineCount
* sizeof (UINTN
) * 3);
840 gBS
->FreePool (OldIndexArray
);
842 switch (StringPtr
[CurrIndex
]) {
846 GlyphWidth
= ((StringPtr
[CurrIndex
] == WIDE_CHAR
) ? 2 : 1);
847 if (CurrIndex
== 0) {
848 LineStartGlyphWidth
= GlyphWidth
;
854 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
858 // Store a range of string as a line
860 IndexArray
[LineCount
*3] = PrevCurrIndex
;
861 IndexArray
[LineCount
*3+1] = CurrIndex
;
862 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
865 // Reset offset and save begin position of line
868 LineStartGlyphWidth
= GlyphWidth
;
869 PrevCurrIndex
= CurrIndex
+ 1;
874 // "\r\n" and "\r" both are handled here
876 case CHAR_CARRIAGE_RETURN
:
877 if (StringPtr
[CurrIndex
+ 1] == CHAR_LINEFEED
) {
881 IndexArray
[LineCount
*3] = PrevCurrIndex
;
882 IndexArray
[LineCount
*3+1] = CurrIndex
;
883 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
888 LineStartGlyphWidth
= GlyphWidth
;
889 PrevCurrIndex
= CurrIndex
+ 1;
893 // char is space or other char
896 GlyphOffset
+= GlyphWidth
;
897 if (GlyphOffset
>= BlockWidth
) {
898 if (LastSpaceIndex
> PrevCurrIndex
) {
900 // LastSpaceIndex points to space inside current screen-line,
901 // restore to LastSpaceIndex
902 // (Otherwise the word is too long to fit one screen-line, just cut it)
904 CurrIndex
= LastSpaceIndex
;
905 GlyphWidth
= LastSpaceGlyphWidth
;
906 } else if (GlyphOffset
> BlockWidth
) {
908 // the word is too long to fit one screen-line and we don't get the chance
909 // of GlyphOffset == BlockWidth because GlyphWidth = 2
914 IndexArray
[LineCount
*3] = PrevCurrIndex
;
915 IndexArray
[LineCount
*3+1] = CurrIndex
+ 1;
916 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
917 LineStartGlyphWidth
= GlyphWidth
;
920 // Reset offset and save begin position of line
923 PrevCurrIndex
= CurrIndex
+ 1;
927 // LastSpaceIndex: remember position of last space
929 if (StringPtr
[CurrIndex
] == CHAR_SPACE
) {
930 LastSpaceIndex
= CurrIndex
;
931 LastSpaceGlyphWidth
= GlyphWidth
;
937 if (GlyphOffset
> 0) {
938 IndexArray
[LineCount
*3] = PrevCurrIndex
;
939 IndexArray
[LineCount
*3+1] = CurrIndex
;
940 IndexArray
[LineCount
*3+2] = GlyphWidth
;
944 if (LineCount
== 0) {
946 // in case we meet null string
951 // we assume null string's glyph width is 1
957 VirtualLineCount
= RowCount
* (LineCount
/ RowCount
+ (LineCount
% RowCount
> 0));
958 *FormattedString
= AllocateZeroPool (VirtualLineCount
* (BlockWidth
+ 1) * sizeof (CHAR16
) * 2);
960 for (CurrIndex
= 0; CurrIndex
< LineCount
; CurrIndex
++) {
961 *(*FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1)) = (CHAR16
) ((IndexArray
[CurrIndex
*3+2] == 2) ? WIDE_CHAR
: NARROW_CHAR
);
963 *FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1) + 1,
964 StringPtr
+ IndexArray
[CurrIndex
*3],
965 IndexArray
[CurrIndex
*3+1]-IndexArray
[CurrIndex
*3]
969 gBS
->FreePool (IndexArray
);