3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Implementation for handling the User Interface option processing.
30 Process Question Config.
32 @param Selection The UI menu selection.
33 @param Question The Question to be peocessed.
35 @retval EFI_SUCCESS Question Config process success.
36 @retval Other Question Config process fail.
40 ProcessQuestionConfig (
41 IN UI_MENU_SELECTION
*Selection
,
42 IN FORM_BROWSER_STATEMENT
*Question
48 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
50 if (Question
->QuestionConfig
== 0) {
57 ConfigResp
= GetToken (Question
->QuestionConfig
, Selection
->FormSet
->HiiHandle
);
58 if (ConfigResp
== NULL
) {
63 // Send config to Configuration Driver
65 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
66 if (ConfigAccess
== NULL
) {
67 return EFI_UNSUPPORTED
;
69 Status
= ConfigAccess
->RouteConfig (
80 Search an Option of a Question by its value.
82 @param Question The Question
83 @param OptionValue Value for Option to be searched.
85 @retval Pointer Pointer to the found Option.
86 @retval NULL Option not found.
91 IN FORM_BROWSER_STATEMENT
*Question
,
92 IN EFI_HII_VALUE
*OptionValue
96 QUESTION_OPTION
*Option
;
98 Link
= GetFirstNode (&Question
->OptionListHead
);
99 while (!IsNull (&Question
->OptionListHead
, Link
)) {
100 Option
= QUESTION_OPTION_FROM_LINK (Link
);
102 if (CompareHiiValue (&Option
->Value
, OptionValue
, NULL
) == 0) {
106 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
114 Print Question Value according to it's storage width and display attributes.
116 @param Event The event to wait for
117 @param FormattedNumber Buffer for output string.
118 @param BufferSize The FormattedNumber buffer size in bytes.
120 @retval EFI_SUCCESS Print success.
121 @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
125 PrintFormattedNumber (
126 IN FORM_BROWSER_STATEMENT
*Question
,
127 IN OUT CHAR16
*FormattedNumber
,
133 EFI_HII_VALUE
*QuestionValue
;
135 if (BufferSize
< (21 * sizeof (CHAR16
))) {
136 return EFI_BUFFER_TOO_SMALL
;
139 QuestionValue
= &Question
->HiiValue
;
141 Value
= (INT64
) QuestionValue
->Value
.u64
;
142 switch (Question
->Flags
& EFI_IFR_DISPLAY
) {
143 case EFI_IFR_DISPLAY_INT_DEC
:
144 switch (QuestionValue
->Type
) {
145 case EFI_IFR_NUMERIC_SIZE_1
:
146 Value
= (INT64
) ((INT8
) QuestionValue
->Value
.u8
);
149 case EFI_IFR_NUMERIC_SIZE_2
:
150 Value
= (INT64
) ((INT16
) QuestionValue
->Value
.u16
);
153 case EFI_IFR_NUMERIC_SIZE_4
:
154 Value
= (INT64
) ((INT32
) QuestionValue
->Value
.u32
);
157 case EFI_IFR_NUMERIC_SIZE_8
:
170 case EFI_IFR_DISPLAY_UINT_DEC
:
174 case EFI_IFR_DISPLAY_UINT_HEX
:
179 return EFI_UNSUPPORTED
;
183 UnicodeSPrint (FormattedNumber
, BufferSize
, Format
, Value
);
190 Password may be stored as encrypted by Configuration Driver. When change a
191 password, user will be challenged with old password. To validate user input old
192 password, we will send the clear text to Configuration Driver via Callback().
193 Configuration driver is responsible to check the passed in password and return
194 the validation result. If validation pass, state machine in password Callback()
195 will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.
196 After user type in new password twice, Callback() will be invoked to send the
197 new password to Configuration Driver.
199 @param Selection Pointer to UI_MENU_SELECTION.
200 @param MenuOption The MenuOption for this password Question.
201 @param String The clear text of password.
203 @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input.
204 @return In state of BROWSER_STATE_VALIDATE_PASSWORD:
205 @retval EFI_SUCCESS Password correct, Browser will prompt for new
207 @retval EFI_NOT_READY Password incorrect, Browser will show error
209 @retval Other Browser will do nothing.
210 @return In state of BROWSER_STATE_SET_PASSWORD:
211 @retval EFI_SUCCESS Set password success.
212 @retval Other Set password failed.
217 IN UI_MENU_SELECTION
*Selection
,
218 IN UI_MENU_OPTION
*MenuOption
,
223 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
224 EFI_BROWSER_ACTION_REQUEST ActionRequest
;
225 EFI_HII_VALUE
*QuestionValue
;
227 QuestionValue
= &MenuOption
->ThisTag
->HiiValue
;
228 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
229 if (ConfigAccess
== NULL
) {
230 return EFI_UNSUPPORTED
;
234 // Prepare password string in HII database
236 if (String
!= NULL
) {
237 QuestionValue
->Value
.string
= NewString (String
, Selection
->FormSet
->HiiHandle
);
239 QuestionValue
->Value
.string
= 0;
243 // Send password to Configuration Driver for validation
245 Status
= ConfigAccess
->Callback (
247 EFI_BROWSER_ACTION_CHANGING
,
248 MenuOption
->ThisTag
->QuestionId
,
250 &QuestionValue
->Value
,
255 // Remove password string from HII database
257 if (String
!= NULL
) {
258 DeleteString (QuestionValue
->Value
.string
, Selection
->FormSet
->HiiHandle
);
266 Display error message for invalid password.
281 // Invalid password, prompt error message
284 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gPassowordInvalid
, gPressEnter
, gEmptyString
);
285 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
290 Process a Question's Option (whether selected or un-selected).
292 @param Selection Pointer to UI_MENU_SELECTION.
293 @param MenuOption The MenuOption for this Question.
294 @param Selected TRUE: if Question is selected.
295 @param OptionString Pointer of the Option String to be displayed.
297 @retval EFI_SUCCESS Question Option process success.
298 @retval Other Question Option process fail.
303 IN UI_MENU_SELECTION
*Selection
,
304 IN UI_MENU_OPTION
*MenuOption
,
306 OUT CHAR16
**OptionString
313 FORM_BROWSER_STATEMENT
*Question
;
314 CHAR16 FormattedNumber
[21];
319 QUESTION_OPTION
*OneOfOption
;
321 EFI_HII_VALUE HiiValue
;
322 EFI_HII_VALUE
*QuestionValue
;
326 Status
= EFI_SUCCESS
;
329 Character
[1] = L
'\0';
330 *OptionString
= NULL
;
332 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
333 BufferSize
= (gOptionBlockWidth
+ 1) * 2 * gScreenDimensions
.BottomRow
;
335 Question
= MenuOption
->ThisTag
;
336 QuestionValue
= &Question
->HiiValue
;
337 Maximum
= (UINT16
) Question
->Maximum
;
339 switch (Question
->Operand
) {
340 case EFI_IFR_ORDERED_LIST_OP
:
342 // Initialize Option value array
344 if (Question
->BufferValue
[0] == 0) {
345 GetQuestionDefault (Selection
->FormSet
, Selection
->Form
, Question
, 0);
352 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
355 // We now know how many strings we will have, so we can allocate the
356 // space required for the array or strings.
358 *OptionString
= AllocateZeroPool (Question
->MaxContainers
* BufferSize
);
359 ASSERT (*OptionString
);
361 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
362 HiiValue
.Value
.u64
= 0;
363 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
364 HiiValue
.Value
.u8
= Question
->BufferValue
[Index
];
365 if (HiiValue
.Value
.u8
== 0) {
367 // Values for the options in ordered lists should never be a 0
372 OneOfOption
= ValueToOption (Question
, &HiiValue
);
373 if (OneOfOption
== NULL
) {
374 gBS
->FreePool (*OptionString
);
375 return EFI_NOT_FOUND
;
379 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
380 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
382 // This option is suppressed
388 Character
[0] = LEFT_ONEOF_DELIMITER
;
389 NewStrCat (OptionString
[0], Character
);
390 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
391 NewStrCat (OptionString
[0], StringPtr
);
392 Character
[0] = RIGHT_ONEOF_DELIMITER
;
393 NewStrCat (OptionString
[0], Character
);
394 Character
[0] = CHAR_CARRIAGE_RETURN
;
395 NewStrCat (OptionString
[0], Character
);
397 gBS
->FreePool (StringPtr
);
403 case EFI_IFR_ONE_OF_OP
:
408 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
410 *OptionString
= AllocateZeroPool (BufferSize
);
411 ASSERT (*OptionString
);
413 OneOfOption
= ValueToOption (Question
, QuestionValue
);
414 if (OneOfOption
== NULL
) {
415 gBS
->FreePool (*OptionString
);
416 return EFI_NOT_FOUND
;
419 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
420 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
422 // This option is suppressed
431 // Current selected option happen to be suppressed,
432 // enforce to select on a non-suppressed option
434 Link
= GetFirstNode (&Question
->OptionListHead
);
435 while (!IsNull (&Question
->OptionListHead
, Link
)) {
436 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
438 if ((OneOfOption
->SuppressExpression
== NULL
) ||
439 !OneOfOption
->SuppressExpression
->Result
.Value
.b
) {
441 CopyMem (QuestionValue
, &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
442 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
446 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
451 Character
[0] = LEFT_ONEOF_DELIMITER
;
452 NewStrCat (OptionString
[0], Character
);
453 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
454 NewStrCat (OptionString
[0], StringPtr
);
455 Character
[0] = RIGHT_ONEOF_DELIMITER
;
456 NewStrCat (OptionString
[0], Character
);
458 gBS
->FreePool (StringPtr
);
463 case EFI_IFR_CHECKBOX_OP
:
464 *OptionString
= AllocateZeroPool (BufferSize
);
465 ASSERT (*OptionString
);
467 *OptionString
[0] = LEFT_CHECKBOX_DELIMITER
;
471 // Since this is a BOOLEAN operation, flip it upon selection
473 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
476 // Perform inconsistent check
478 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
479 if (EFI_ERROR (Status
)) {
481 // Inconsistent check fail, restore Question Value
483 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
484 gBS
->FreePool (*OptionString
);
489 // Save Question value
491 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
492 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
495 if (QuestionValue
->Value
.b
) {
496 *(OptionString
[0] + 1) = CHECK_ON
;
498 *(OptionString
[0] + 1) = CHECK_OFF
;
500 *(OptionString
[0] + 2) = RIGHT_CHECKBOX_DELIMITER
;
503 case EFI_IFR_NUMERIC_OP
:
508 Status
= GetNumericInput (Selection
, MenuOption
);
510 *OptionString
= AllocateZeroPool (BufferSize
);
511 ASSERT (*OptionString
);
513 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
518 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
519 Number
= (UINT16
) GetStringWidth (FormattedNumber
);
520 CopyMem (OptionString
[0] + 1, FormattedNumber
, Number
);
522 *(OptionString
[0] + Number
/ 2) = RIGHT_NUMERIC_DELIMITER
;
526 case EFI_IFR_DATE_OP
:
529 // This is similar to numerics
531 Status
= GetNumericInput (Selection
, MenuOption
);
533 *OptionString
= AllocateZeroPool (BufferSize
);
534 ASSERT (*OptionString
);
536 switch (MenuOption
->Sequence
) {
538 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
539 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Month
);
540 *(OptionString
[0] + 3) = DATE_SEPARATOR
;
544 SetUnicodeMem (OptionString
[0], 4, L
' ');
545 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Day
);
546 *(OptionString
[0] + 6) = DATE_SEPARATOR
;
550 SetUnicodeMem (OptionString
[0], 7, L
' ');
551 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%4d", QuestionValue
->Value
.date
.Year
);
552 *(OptionString
[0] + 11) = RIGHT_NUMERIC_DELIMITER
;
558 case EFI_IFR_TIME_OP
:
561 // This is similar to numerics
563 Status
= GetNumericInput (Selection
, MenuOption
);
565 *OptionString
= AllocateZeroPool (BufferSize
);
566 ASSERT (*OptionString
);
568 switch (MenuOption
->Sequence
) {
570 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
571 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Hour
);
572 *(OptionString
[0] + 3) = TIME_SEPARATOR
;
576 SetUnicodeMem (OptionString
[0], 4, L
' ');
577 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Minute
);
578 *(OptionString
[0] + 6) = TIME_SEPARATOR
;
582 SetUnicodeMem (OptionString
[0], 7, L
' ');
583 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Second
);
584 *(OptionString
[0] + 9) = RIGHT_NUMERIC_DELIMITER
;
590 case EFI_IFR_STRING_OP
:
592 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
595 Status
= ReadString (MenuOption
, gPromptForData
, StringPtr
);
596 if (!EFI_ERROR (Status
)) {
597 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
598 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
600 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
603 gBS
->FreePool (StringPtr
);
605 *OptionString
= AllocateZeroPool (BufferSize
);
606 ASSERT (*OptionString
);
608 if (((CHAR16
*) Question
->BufferValue
)[0] == 0x0000) {
609 *(OptionString
[0]) = '_';
611 if ((Maximum
* sizeof (CHAR16
)) < BufferSize
) {
612 BufferSize
= Maximum
* sizeof (CHAR16
);
614 CopyMem (OptionString
[0], (CHAR16
*) Question
->BufferValue
, BufferSize
);
619 case EFI_IFR_PASSWORD_OP
:
621 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
625 // For interactive passwords, old password is validated by callback
627 if (Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) {
629 // Use a NULL password to test whether old password is required
632 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
633 if (Status
== EFI_NOT_AVAILABLE_YET
) {
635 // Callback request to terminate password input
637 gBS
->FreePool (StringPtr
);
641 if (EFI_ERROR (Status
)) {
643 // Old password exist, ask user for the old password
645 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
646 if (EFI_ERROR (Status
)) {
647 gBS
->FreePool (StringPtr
);
652 // Check user input old password
654 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
655 if (EFI_ERROR (Status
)) {
656 if (Status
== EFI_NOT_READY
) {
658 // Typed in old password incorrect
662 Status
= EFI_SUCCESS
;
665 gBS
->FreePool (StringPtr
);
671 // For non-interactive password, validate old password in local
673 if (*((CHAR16
*) Question
->BufferValue
) != 0) {
675 // There is something there! Prompt for password
677 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
678 if (EFI_ERROR (Status
)) {
679 gBS
->FreePool (StringPtr
);
683 TempString
= AllocateCopyPool ((Maximum
+ 1) * sizeof (CHAR16
), Question
->BufferValue
);
684 TempString
[Maximum
] = L
'\0';
686 if (StrCmp (StringPtr
, TempString
) != 0) {
688 // Typed in old password incorrect
692 gBS
->FreePool (StringPtr
);
693 gBS
->FreePool (TempString
);
697 gBS
->FreePool (TempString
);
702 // Ask for new password
704 ZeroMem (StringPtr
, (Maximum
+ 1) * sizeof (CHAR16
));
705 Status
= ReadString (MenuOption
, gPromptForNewPassword
, StringPtr
);
706 if (EFI_ERROR (Status
)) {
708 // Reset state machine for interactive password
710 if (Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) {
711 PasswordCallback (Selection
, MenuOption
, NULL
);
714 gBS
->FreePool (StringPtr
);
719 // Confirm new password
721 TempString
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
723 Status
= ReadString (MenuOption
, gConfirmPassword
, TempString
);
724 if (EFI_ERROR (Status
)) {
726 // Reset state machine for interactive password
728 if (Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) {
729 PasswordCallback (Selection
, MenuOption
, NULL
);
732 gBS
->FreePool (StringPtr
);
733 gBS
->FreePool (TempString
);
738 // Compare two typed-in new passwords
740 if (StrCmp (StringPtr
, TempString
) == 0) {
742 // Two password match, send it to Configuration Driver
744 if (Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) {
745 PasswordCallback (Selection
, MenuOption
, StringPtr
);
747 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
748 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
752 // Reset state machine for interactive password
754 if (Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) {
755 PasswordCallback (Selection
, MenuOption
, NULL
);
759 // Two password mismatch, prompt error message
762 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gConfirmError
, gPressEnter
, gEmptyString
);
763 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
766 gBS
->FreePool (TempString
);
767 gBS
->FreePool (StringPtr
);
780 Process the help string: Split StringPtr to several lines of strings stored in
781 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
783 @param StringPtr The entire help string.
784 @param MenuOption The MenuOption for this Question.
785 @param RowCount TRUE: if Question is selected.
786 @param OptionString Pointer of the Option String to be displayed.
793 IN CHAR16
*StringPtr
,
794 OUT CHAR16
**FormattedString
,
798 CONST UINTN BlockWidth
= (UINTN
) gHelpBlockWidth
- 1;
801 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
806 UINTN VirtualLineCount
;
808 // GlyphOffset stores glyph width of current screen-line
812 // GlyphWidth equals to 2 if we meet width directive
816 // during scanning, we remember the position of last space character
817 // in case that if next word cannot put in current line, we could restore back to the position
818 // of last space character
819 // while we should also remmeber the glyph width of the last space character for restoring
821 UINTN LastSpaceIndex
;
822 UINTN LastSpaceGlyphWidth
;
824 // every time we begin to form a new screen-line, we should remember glyph width of single character
827 UINTN LineStartGlyphWidth
;
829 UINTN
*OldIndexArray
;
832 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
833 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
834 // to bring the width directive of the last line to current screen-line.
835 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
836 // different from that of "\wideabcde", we should remember the width directive.
839 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
841 if (*FormattedString
!= NULL
) {
842 gBS
->FreePool (*FormattedString
);
843 *FormattedString
= NULL
;
846 for (PrevCurrIndex
= 0, CurrIndex
= 0, LineCount
= 0, LastSpaceIndex
= 0,
847 IndexArray
[0] = 0, GlyphWidth
= 1, GlyphOffset
= 0, LastSpaceGlyphWidth
= 1, LineStartGlyphWidth
= 1;
848 (StringPtr
[CurrIndex
] != CHAR_NULL
);
851 if (LineCount
== AllocateSize
) {
852 AllocateSize
+= 0x10;
853 OldIndexArray
= IndexArray
;
854 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
855 CopyMem (IndexArray
, OldIndexArray
, LineCount
* sizeof (UINTN
) * 3);
856 gBS
->FreePool (OldIndexArray
);
858 switch (StringPtr
[CurrIndex
]) {
862 GlyphWidth
= ((StringPtr
[CurrIndex
] == WIDE_CHAR
) ? 2 : 1);
863 if (CurrIndex
== 0) {
864 LineStartGlyphWidth
= GlyphWidth
;
870 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
874 // Store a range of string as a line
876 IndexArray
[LineCount
*3] = PrevCurrIndex
;
877 IndexArray
[LineCount
*3+1] = CurrIndex
;
878 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
881 // Reset offset and save begin position of line
884 LineStartGlyphWidth
= GlyphWidth
;
885 PrevCurrIndex
= CurrIndex
+ 1;
890 // "\r\n" and "\r" both are handled here
892 case CHAR_CARRIAGE_RETURN
:
893 if (StringPtr
[CurrIndex
+ 1] == CHAR_LINEFEED
) {
897 IndexArray
[LineCount
*3] = PrevCurrIndex
;
898 IndexArray
[LineCount
*3+1] = CurrIndex
;
899 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
904 LineStartGlyphWidth
= GlyphWidth
;
905 PrevCurrIndex
= CurrIndex
+ 1;
909 // char is space or other char
912 GlyphOffset
+= GlyphWidth
;
913 if (GlyphOffset
>= BlockWidth
) {
914 if (LastSpaceIndex
> PrevCurrIndex
) {
916 // LastSpaceIndex points to space inside current screen-line,
917 // restore to LastSpaceIndex
918 // (Otherwise the word is too long to fit one screen-line, just cut it)
920 CurrIndex
= LastSpaceIndex
;
921 GlyphWidth
= LastSpaceGlyphWidth
;
922 } else if (GlyphOffset
> BlockWidth
) {
924 // the word is too long to fit one screen-line and we don't get the chance
925 // of GlyphOffset == BlockWidth because GlyphWidth = 2
930 IndexArray
[LineCount
*3] = PrevCurrIndex
;
931 IndexArray
[LineCount
*3+1] = CurrIndex
+ 1;
932 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
933 LineStartGlyphWidth
= GlyphWidth
;
936 // Reset offset and save begin position of line
939 PrevCurrIndex
= CurrIndex
+ 1;
943 // LastSpaceIndex: remember position of last space
945 if (StringPtr
[CurrIndex
] == CHAR_SPACE
) {
946 LastSpaceIndex
= CurrIndex
;
947 LastSpaceGlyphWidth
= GlyphWidth
;
953 if (GlyphOffset
> 0) {
954 IndexArray
[LineCount
*3] = PrevCurrIndex
;
955 IndexArray
[LineCount
*3+1] = CurrIndex
;
956 IndexArray
[LineCount
*3+2] = GlyphWidth
;
960 if (LineCount
== 0) {
962 // in case we meet null string
967 // we assume null string's glyph width is 1
973 VirtualLineCount
= RowCount
* (LineCount
/ RowCount
+ (LineCount
% RowCount
> 0));
974 *FormattedString
= AllocateZeroPool (VirtualLineCount
* (BlockWidth
+ 1) * sizeof (CHAR16
) * 2);
976 for (CurrIndex
= 0; CurrIndex
< LineCount
; CurrIndex
++) {
977 *(*FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1)) = (CHAR16
) ((IndexArray
[CurrIndex
*3+2] == 2) ? WIDE_CHAR
: NARROW_CHAR
);
979 *FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1) + 1,
980 StringPtr
+ IndexArray
[CurrIndex
*3],
981 IndexArray
[CurrIndex
*3+1]-IndexArray
[CurrIndex
*3]
985 gBS
->FreePool (IndexArray
);