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
332 if (Question
->BufferValue
[0] == 0) {
333 GetQuestionDefault (Selection
->FormSet
, Selection
->Form
, Question
, 0);
340 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
343 // We now know how many strings we will have, so we can allocate the
344 // space required for the array or strings.
346 *OptionString
= AllocateZeroPool (Question
->MaxContainers
* BufferSize
);
347 ASSERT (*OptionString
);
349 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
350 HiiValue
.Value
.u64
= 0;
351 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
352 HiiValue
.Value
.u8
= Question
->BufferValue
[Index
];
353 if (HiiValue
.Value
.u8
== 0) {
355 // Values for the options in ordered lists should never be a 0
360 OneOfOption
= ValueToOption (Question
, &HiiValue
);
361 if (OneOfOption
== NULL
) {
362 gBS
->FreePool (*OptionString
);
363 return EFI_NOT_FOUND
;
367 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
368 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
370 // This option is suppressed
376 Character
[0] = LEFT_ONEOF_DELIMITER
;
377 NewStrCat (OptionString
[0], Character
);
378 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
379 NewStrCat (OptionString
[0], StringPtr
);
380 Character
[0] = RIGHT_ONEOF_DELIMITER
;
381 NewStrCat (OptionString
[0], Character
);
382 Character
[0] = CHAR_CARRIAGE_RETURN
;
383 NewStrCat (OptionString
[0], Character
);
385 gBS
->FreePool (StringPtr
);
391 case EFI_IFR_ONE_OF_OP
:
396 Status
= GetSelectionInputPopUp (Selection
, MenuOption
);
398 *OptionString
= AllocateZeroPool (BufferSize
);
399 ASSERT (*OptionString
);
401 OneOfOption
= ValueToOption (Question
, QuestionValue
);
402 if (OneOfOption
== NULL
) {
403 gBS
->FreePool (*OptionString
);
404 return EFI_NOT_FOUND
;
407 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
408 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
410 // This option is suppressed
419 // Current selected option happen to be suppressed,
420 // enforce to select on a non-suppressed option
422 Link
= GetFirstNode (&Question
->OptionListHead
);
423 while (!IsNull (&Question
->OptionListHead
, Link
)) {
424 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
426 if ((OneOfOption
->SuppressExpression
== NULL
) ||
427 !OneOfOption
->SuppressExpression
->Result
.Value
.b
) {
429 CopyMem (QuestionValue
, &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
430 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
434 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
439 Character
[0] = LEFT_ONEOF_DELIMITER
;
440 NewStrCat (OptionString
[0], Character
);
441 StringPtr
= GetToken (OneOfOption
->Text
, Selection
->Handle
);
442 NewStrCat (OptionString
[0], StringPtr
);
443 Character
[0] = RIGHT_ONEOF_DELIMITER
;
444 NewStrCat (OptionString
[0], Character
);
446 gBS
->FreePool (StringPtr
);
451 case EFI_IFR_CHECKBOX_OP
:
452 *OptionString
= AllocateZeroPool (BufferSize
);
453 ASSERT (*OptionString
);
455 *OptionString
[0] = LEFT_CHECKBOX_DELIMITER
;
459 // Since this is a BOOLEAN operation, flip it upon selection
461 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
464 // Perform inconsistent check
466 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
467 if (EFI_ERROR (Status
)) {
469 // Inconsistent check fail, restore Question Value
471 QuestionValue
->Value
.b
= (BOOLEAN
) (QuestionValue
->Value
.b
? FALSE
: TRUE
);
472 gBS
->FreePool (*OptionString
);
477 // Save Question value
479 Status
= SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
480 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
483 if (QuestionValue
->Value
.b
) {
484 *(OptionString
[0] + 1) = CHECK_ON
;
486 *(OptionString
[0] + 1) = CHECK_OFF
;
488 *(OptionString
[0] + 2) = RIGHT_CHECKBOX_DELIMITER
;
491 case EFI_IFR_NUMERIC_OP
:
496 Status
= GetNumericInput (Selection
, MenuOption
);
498 *OptionString
= AllocateZeroPool (BufferSize
);
499 ASSERT (*OptionString
);
501 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
506 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
507 Number
= (UINT16
) GetStringWidth (FormattedNumber
);
508 CopyMem (OptionString
[0] + 1, FormattedNumber
, Number
);
510 *(OptionString
[0] + Number
/ 2) = RIGHT_NUMERIC_DELIMITER
;
514 case EFI_IFR_DATE_OP
:
517 // This is similar to numerics
519 Status
= GetNumericInput (Selection
, MenuOption
);
521 *OptionString
= AllocateZeroPool (BufferSize
);
522 ASSERT (*OptionString
);
524 switch (MenuOption
->Sequence
) {
526 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
527 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Month
);
528 *(OptionString
[0] + 3) = DATE_SEPARATOR
;
532 SetUnicodeMem (OptionString
[0], 4, L
' ');
533 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.date
.Day
);
534 *(OptionString
[0] + 6) = DATE_SEPARATOR
;
538 SetUnicodeMem (OptionString
[0], 7, L
' ');
539 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%4d", QuestionValue
->Value
.date
.Year
);
540 *(OptionString
[0] + 11) = RIGHT_NUMERIC_DELIMITER
;
546 case EFI_IFR_TIME_OP
:
549 // This is similar to numerics
551 Status
= GetNumericInput (Selection
, MenuOption
);
553 *OptionString
= AllocateZeroPool (BufferSize
);
554 ASSERT (*OptionString
);
556 switch (MenuOption
->Sequence
) {
558 *OptionString
[0] = LEFT_NUMERIC_DELIMITER
;
559 UnicodeSPrint (OptionString
[0] + 1, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Hour
);
560 *(OptionString
[0] + 3) = TIME_SEPARATOR
;
564 SetUnicodeMem (OptionString
[0], 4, L
' ');
565 UnicodeSPrint (OptionString
[0] + 4, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Minute
);
566 *(OptionString
[0] + 6) = TIME_SEPARATOR
;
570 SetUnicodeMem (OptionString
[0], 7, L
' ');
571 UnicodeSPrint (OptionString
[0] + 7, 21 * sizeof (CHAR16
), L
"%02d", QuestionValue
->Value
.time
.Second
);
572 *(OptionString
[0] + 9) = RIGHT_NUMERIC_DELIMITER
;
578 case EFI_IFR_STRING_OP
:
580 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
583 Status
= ReadString (MenuOption
, gPromptForData
, StringPtr
);
584 if (!EFI_ERROR (Status
)) {
585 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
586 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
588 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
591 gBS
->FreePool (StringPtr
);
593 *OptionString
= AllocateZeroPool (BufferSize
);
594 ASSERT (*OptionString
);
596 if (((CHAR16
*) Question
->BufferValue
)[0] == 0x0000) {
597 *(OptionString
[0]) = '_';
599 if ((Maximum
* sizeof (CHAR16
)) < BufferSize
) {
600 BufferSize
= Maximum
* sizeof (CHAR16
);
602 CopyMem (OptionString
[0], (CHAR16
*) Question
->BufferValue
, BufferSize
);
607 case EFI_IFR_PASSWORD_OP
:
609 StringPtr
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
613 // For interactive passwords, old password is validated by callback
615 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
617 // Use a NULL password to test whether old password is required
620 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
621 if (Status
== EFI_NOT_AVAILABLE_YET
) {
623 // Callback request to terminate password input
625 gBS
->FreePool (StringPtr
);
629 if (EFI_ERROR (Status
)) {
631 // Old password exist, ask user for the old password
633 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
634 if (EFI_ERROR (Status
)) {
635 gBS
->FreePool (StringPtr
);
640 // Check user input old password
642 Status
= PasswordCallback (Selection
, MenuOption
, StringPtr
);
643 if (EFI_ERROR (Status
)) {
644 if (Status
== EFI_NOT_READY
) {
646 // Typed in old password incorrect
650 Status
= EFI_SUCCESS
;
653 gBS
->FreePool (StringPtr
);
659 // For non-interactive password, validate old password in local
661 if (*((CHAR16
*) Question
->BufferValue
) != 0) {
663 // There is something there! Prompt for password
665 Status
= ReadString (MenuOption
, gPromptForPassword
, StringPtr
);
666 if (EFI_ERROR (Status
)) {
667 gBS
->FreePool (StringPtr
);
671 TempString
= AllocateCopyPool ((Maximum
+ 1) * sizeof (CHAR16
), Question
->BufferValue
);
672 TempString
[Maximum
] = L
'\0';
674 if (StrCmp (StringPtr
, TempString
) != 0) {
676 // Typed in old password incorrect
680 gBS
->FreePool (StringPtr
);
681 gBS
->FreePool (TempString
);
685 gBS
->FreePool (TempString
);
690 // Ask for new password
692 ZeroMem (StringPtr
, (Maximum
+ 1) * sizeof (CHAR16
));
693 Status
= ReadString (MenuOption
, gPromptForNewPassword
, StringPtr
);
694 if (EFI_ERROR (Status
)) {
696 // Reset state machine for interactive password
698 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
699 PasswordCallback (Selection
, MenuOption
, NULL
);
702 gBS
->FreePool (StringPtr
);
707 // Confirm new password
709 TempString
= AllocateZeroPool ((Maximum
+ 1) * sizeof (CHAR16
));
711 Status
= ReadString (MenuOption
, gConfirmPassword
, TempString
);
712 if (EFI_ERROR (Status
)) {
714 // Reset state machine for interactive password
716 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
717 PasswordCallback (Selection
, MenuOption
, NULL
);
720 gBS
->FreePool (StringPtr
);
721 gBS
->FreePool (TempString
);
726 // Compare two typed-in new passwords
728 if (StrCmp (StringPtr
, TempString
) == 0) {
730 // Two password match, send it to Configuration Driver
732 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
733 PasswordCallback (Selection
, MenuOption
, StringPtr
);
735 CopyMem (Question
->BufferValue
, StringPtr
, Maximum
* sizeof (CHAR16
));
736 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
740 // Reset state machine for interactive password
742 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
743 PasswordCallback (Selection
, MenuOption
, NULL
);
747 // Two password mismatch, prompt error message
750 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gConfirmError
, gPressEnter
, gEmptyString
);
751 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
754 gBS
->FreePool (TempString
);
755 gBS
->FreePool (StringPtr
);
768 Process the help string: Split StringPtr to several lines of strings stored in
769 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
771 @param StringPtr The entire help string.
772 @param FormattedString The oupput formatted string.
773 @param RowCount TRUE: if Question is selected.
778 IN CHAR16
*StringPtr
,
779 OUT CHAR16
**FormattedString
,
783 CONST UINTN BlockWidth
= (UINTN
) gHelpBlockWidth
- 1;
786 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
791 UINTN VirtualLineCount
;
793 // GlyphOffset stores glyph width of current screen-line
797 // GlyphWidth equals to 2 if we meet width directive
801 // during scanning, we remember the position of last space character
802 // in case that if next word cannot put in current line, we could restore back to the position
803 // of last space character
804 // while we should also remmeber the glyph width of the last space character for restoring
806 UINTN LastSpaceIndex
;
807 UINTN LastSpaceGlyphWidth
;
809 // every time we begin to form a new screen-line, we should remember glyph width of single character
812 UINTN LineStartGlyphWidth
;
814 UINTN
*OldIndexArray
;
817 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
818 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
819 // to bring the width directive of the last line to current screen-line.
820 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
821 // different from that of "\wideabcde", we should remember the width directive.
824 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
826 if (*FormattedString
!= NULL
) {
827 gBS
->FreePool (*FormattedString
);
828 *FormattedString
= NULL
;
831 for (PrevCurrIndex
= 0, CurrIndex
= 0, LineCount
= 0, LastSpaceIndex
= 0,
832 IndexArray
[0] = 0, GlyphWidth
= 1, GlyphOffset
= 0, LastSpaceGlyphWidth
= 1, LineStartGlyphWidth
= 1;
833 (StringPtr
[CurrIndex
] != CHAR_NULL
);
836 if (LineCount
== AllocateSize
) {
837 AllocateSize
+= 0x10;
838 OldIndexArray
= IndexArray
;
839 IndexArray
= AllocatePool (AllocateSize
* sizeof (UINTN
) * 3);
840 CopyMem (IndexArray
, OldIndexArray
, LineCount
* sizeof (UINTN
) * 3);
841 gBS
->FreePool (OldIndexArray
);
843 switch (StringPtr
[CurrIndex
]) {
847 GlyphWidth
= ((StringPtr
[CurrIndex
] == WIDE_CHAR
) ? 2 : 1);
848 if (CurrIndex
== 0) {
849 LineStartGlyphWidth
= GlyphWidth
;
855 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
859 // Store a range of string as a line
861 IndexArray
[LineCount
*3] = PrevCurrIndex
;
862 IndexArray
[LineCount
*3+1] = CurrIndex
;
863 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
866 // Reset offset and save begin position of line
869 LineStartGlyphWidth
= GlyphWidth
;
870 PrevCurrIndex
= CurrIndex
+ 1;
875 // "\r\n" and "\r" both are handled here
877 case CHAR_CARRIAGE_RETURN
:
878 if (StringPtr
[CurrIndex
+ 1] == CHAR_LINEFEED
) {
882 IndexArray
[LineCount
*3] = PrevCurrIndex
;
883 IndexArray
[LineCount
*3+1] = CurrIndex
;
884 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
889 LineStartGlyphWidth
= GlyphWidth
;
890 PrevCurrIndex
= CurrIndex
+ 1;
894 // char is space or other char
897 GlyphOffset
+= GlyphWidth
;
898 if (GlyphOffset
>= BlockWidth
) {
899 if (LastSpaceIndex
> PrevCurrIndex
) {
901 // LastSpaceIndex points to space inside current screen-line,
902 // restore to LastSpaceIndex
903 // (Otherwise the word is too long to fit one screen-line, just cut it)
905 CurrIndex
= LastSpaceIndex
;
906 GlyphWidth
= LastSpaceGlyphWidth
;
907 } else if (GlyphOffset
> BlockWidth
) {
909 // the word is too long to fit one screen-line and we don't get the chance
910 // of GlyphOffset == BlockWidth because GlyphWidth = 2
915 IndexArray
[LineCount
*3] = PrevCurrIndex
;
916 IndexArray
[LineCount
*3+1] = CurrIndex
+ 1;
917 IndexArray
[LineCount
*3+2] = LineStartGlyphWidth
;
918 LineStartGlyphWidth
= GlyphWidth
;
921 // Reset offset and save begin position of line
924 PrevCurrIndex
= CurrIndex
+ 1;
928 // LastSpaceIndex: remember position of last space
930 if (StringPtr
[CurrIndex
] == CHAR_SPACE
) {
931 LastSpaceIndex
= CurrIndex
;
932 LastSpaceGlyphWidth
= GlyphWidth
;
938 if (GlyphOffset
> 0) {
939 IndexArray
[LineCount
*3] = PrevCurrIndex
;
940 IndexArray
[LineCount
*3+1] = CurrIndex
;
941 IndexArray
[LineCount
*3+2] = GlyphWidth
;
945 if (LineCount
== 0) {
947 // in case we meet null string
952 // we assume null string's glyph width is 1
958 VirtualLineCount
= RowCount
* (LineCount
/ RowCount
+ (LineCount
% RowCount
> 0));
959 *FormattedString
= AllocateZeroPool (VirtualLineCount
* (BlockWidth
+ 1) * sizeof (CHAR16
) * 2);
961 for (CurrIndex
= 0; CurrIndex
< LineCount
; CurrIndex
++) {
962 *(*FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1)) = (CHAR16
) ((IndexArray
[CurrIndex
*3+2] == 2) ? WIDE_CHAR
: NARROW_CHAR
);
964 *FormattedString
+ CurrIndex
* 2 * (BlockWidth
+ 1) + 1,
965 StringPtr
+ IndexArray
[CurrIndex
*3],
966 IndexArray
[CurrIndex
*3+1]-IndexArray
[CurrIndex
*3]
970 gBS
->FreePool (IndexArray
);