]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
6e851ed7bdeeade944cf02e02297fb72fc1c0e16
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / Ui.c
1 /** @file
2 Utility functions for User Interface functions.
3
4 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Setup.h"
16
17 LIST_ENTRY gMenuOption;
18 LIST_ENTRY gMenuList = INITIALIZE_LIST_HEAD_VARIABLE (gMenuList);
19 MENU_REFRESH_ENTRY *gMenuRefreshHead;
20
21 //
22 // Search table for UiDisplayMenu()
23 //
24 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
25 {
26 SCAN_UP,
27 UiUp,
28 },
29 {
30 SCAN_DOWN,
31 UiDown,
32 },
33 {
34 SCAN_PAGE_UP,
35 UiPageUp,
36 },
37 {
38 SCAN_PAGE_DOWN,
39 UiPageDown,
40 },
41 {
42 SCAN_ESC,
43 UiReset,
44 },
45 {
46 SCAN_LEFT,
47 UiLeft,
48 },
49 {
50 SCAN_RIGHT,
51 UiRight,
52 },
53 {
54 SCAN_F9,
55 UiDefault,
56 },
57 {
58 SCAN_F10,
59 UiSave
60 }
61 };
62
63 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
64 {
65 UiNoOperation,
66 CfUiNoOperation,
67 },
68 {
69 UiDefault,
70 CfUiDefault,
71 },
72 {
73 UiSelect,
74 CfUiSelect,
75 },
76 {
77 UiUp,
78 CfUiUp,
79 },
80 {
81 UiDown,
82 CfUiDown,
83 },
84 {
85 UiLeft,
86 CfUiLeft,
87 },
88 {
89 UiRight,
90 CfUiRight,
91 },
92 {
93 UiReset,
94 CfUiReset,
95 },
96 {
97 UiSave,
98 CfUiSave,
99 },
100 {
101 UiPageUp,
102 CfUiPageUp,
103 },
104 {
105 UiPageDown,
106 CfUiPageDown
107 }
108 };
109
110 BOOLEAN mInputError;
111 BOOLEAN GetLineByWidthFinished = FALSE;
112
113
114 /**
115 Set Buffer to Value for Size bytes.
116
117 @param Buffer Memory to set.
118 @param Size Number of bytes to set
119 @param Value Value of the set operation.
120
121 **/
122 VOID
123 SetUnicodeMem (
124 IN VOID *Buffer,
125 IN UINTN Size,
126 IN CHAR16 Value
127 )
128 {
129 CHAR16 *Ptr;
130
131 Ptr = Buffer;
132 while ((Size--) != 0) {
133 *(Ptr++) = Value;
134 }
135 }
136
137
138 /**
139 Initialize Menu option list.
140
141 **/
142 VOID
143 UiInitMenu (
144 VOID
145 )
146 {
147 InitializeListHead (&gMenuOption);
148 }
149
150
151 /**
152 Free Menu option linked list.
153
154 **/
155 VOID
156 UiFreeMenu (
157 VOID
158 )
159 {
160 UI_MENU_OPTION *MenuOption;
161
162 while (!IsListEmpty (&gMenuOption)) {
163 MenuOption = MENU_OPTION_FROM_LINK (gMenuOption.ForwardLink);
164 RemoveEntryList (&MenuOption->Link);
165
166 //
167 // We allocated space for this description when we did a GetToken, free it here
168 //
169 if (MenuOption->Skip != 0) {
170 //
171 // For date/time, MenuOption->Description is shared by three Menu Options
172 // Data format : [01/02/2004] [11:22:33]
173 // Line number : 0 0 1 0 0 1
174 //
175 FreePool (MenuOption->Description);
176 }
177 FreePool (MenuOption);
178 }
179 }
180
181
182 /**
183 Create a menu with specified formset GUID and form ID, and add it as a child
184 of the given parent menu.
185
186 @param Parent The parent of menu to be added.
187 @param FormSetGuid The Formset Guid of menu to be added.
188 @param FormId The Form ID of menu to be added.
189
190 @return A pointer to the newly added menu or NULL if memory is insufficient.
191
192 **/
193 UI_MENU_LIST *
194 UiAddMenuList (
195 IN OUT UI_MENU_LIST *Parent,
196 IN EFI_GUID *FormSetGuid,
197 IN UINT16 FormId
198 )
199 {
200 UI_MENU_LIST *MenuList;
201
202 MenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
203 if (MenuList == NULL) {
204 return NULL;
205 }
206
207 MenuList->Signature = UI_MENU_LIST_SIGNATURE;
208 InitializeListHead (&MenuList->ChildListHead);
209
210 CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID));
211 MenuList->FormId = FormId;
212 MenuList->Parent = Parent;
213
214 if (Parent == NULL) {
215 //
216 // If parent is not specified, it is the root Form of a Formset
217 //
218 InsertTailList (&gMenuList, &MenuList->Link);
219 } else {
220 InsertTailList (&Parent->ChildListHead, &MenuList->Link);
221 }
222
223 return MenuList;
224 }
225
226
227 /**
228 Search Menu with given FormId in the parent menu and all its child menus.
229
230 @param Parent The parent of menu to search.
231 @param FormId The Form ID of menu to search.
232
233 @return A pointer to menu found or NULL if not found.
234
235 **/
236 UI_MENU_LIST *
237 UiFindChildMenuList (
238 IN UI_MENU_LIST *Parent,
239 IN UINT16 FormId
240 )
241 {
242 LIST_ENTRY *Link;
243 UI_MENU_LIST *Child;
244 UI_MENU_LIST *MenuList;
245
246 if (Parent->FormId == FormId) {
247 return Parent;
248 }
249
250 Link = GetFirstNode (&Parent->ChildListHead);
251 while (!IsNull (&Parent->ChildListHead, Link)) {
252 Child = UI_MENU_LIST_FROM_LINK (Link);
253
254 MenuList = UiFindChildMenuList (Child, FormId);
255 if (MenuList != NULL) {
256 return MenuList;
257 }
258
259 Link = GetNextNode (&Parent->ChildListHead, Link);
260 }
261
262 return NULL;
263 }
264
265
266 /**
267 Search Menu with given FormSetGuid and FormId in all cached menu list.
268
269 @param FormSetGuid The Formset GUID of the menu to search.
270 @param FormId The Form ID of menu to search.
271
272 @return A pointer to menu found or NULL if not found.
273
274 **/
275 UI_MENU_LIST *
276 UiFindMenuList (
277 IN EFI_GUID *FormSetGuid,
278 IN UINT16 FormId
279 )
280 {
281 LIST_ENTRY *Link;
282 UI_MENU_LIST *MenuList;
283 UI_MENU_LIST *Child;
284
285 Link = GetFirstNode (&gMenuList);
286 while (!IsNull (&gMenuList, Link)) {
287 MenuList = UI_MENU_LIST_FROM_LINK (Link);
288
289 if (CompareGuid (FormSetGuid, &MenuList->FormSetGuid)) {
290 //
291 // This is the formset we are looking for, find the form in this formset
292 //
293 Child = UiFindChildMenuList (MenuList, FormId);
294 if (Child != NULL) {
295 return Child;
296 }
297 }
298
299 Link = GetNextNode (&gMenuList, Link);
300 }
301
302 return NULL;
303 }
304
305
306 /**
307 Free Menu option linked list.
308
309 **/
310 VOID
311 UiFreeRefreshList (
312 VOID
313 )
314 {
315 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
316
317 while (gMenuRefreshHead != NULL) {
318 OldMenuRefreshEntry = gMenuRefreshHead->Next;
319 FreePool (gMenuRefreshHead);
320 gMenuRefreshHead = OldMenuRefreshEntry;
321 }
322
323 gMenuRefreshHead = NULL;
324 }
325
326
327
328 /**
329 Refresh screen.
330
331 **/
332 EFI_STATUS
333 RefreshForm (
334 VOID
335 )
336 {
337 CHAR16 *OptionString;
338 MENU_REFRESH_ENTRY *MenuRefreshEntry;
339 UINTN Index;
340 EFI_STATUS Status;
341 UI_MENU_SELECTION *Selection;
342 FORM_BROWSER_STATEMENT *Question;
343 EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
344 EFI_HII_VALUE *HiiValue;
345 EFI_BROWSER_ACTION_REQUEST ActionRequest;
346
347 if (gMenuRefreshHead != NULL) {
348
349 MenuRefreshEntry = gMenuRefreshHead;
350
351 //
352 // Reset FormPackage update flag
353 //
354 mHiiPackageListUpdated = FALSE;
355
356 do {
357 gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
358
359 Selection = MenuRefreshEntry->Selection;
360 Question = MenuRefreshEntry->MenuOption->ThisTag;
361
362 Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
363 if (EFI_ERROR (Status)) {
364 return Status;
365 }
366
367 OptionString = NULL;
368 ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
369
370 if (OptionString != NULL) {
371 //
372 // If leading spaces on OptionString - remove the spaces
373 //
374 for (Index = 0; OptionString[Index] == L' '; Index++)
375 ;
376
377 PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, &OptionString[Index]);
378 FreePool (OptionString);
379 }
380
381 //
382 // Question value may be changed, need invoke its Callback()
383 //
384 ConfigAccess = Selection->FormSet->ConfigAccess;
385 if (((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) && (ConfigAccess != NULL)) {
386 ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
387
388 HiiValue = &Question->HiiValue;
389 if (HiiValue->Type == EFI_IFR_TYPE_STRING) {
390 //
391 // Create String in HII database for Configuration Driver to retrieve
392 //
393 HiiValue->Value.string = NewString ((CHAR16 *) Question->BufferValue, Selection->FormSet->HiiHandle);
394 }
395
396 Status = ConfigAccess->Callback (
397 ConfigAccess,
398 EFI_BROWSER_ACTION_CHANGING,
399 Question->QuestionId,
400 HiiValue->Type,
401 &HiiValue->Value,
402 &ActionRequest
403 );
404
405 if (HiiValue->Type == EFI_IFR_TYPE_STRING) {
406 //
407 // Clean the String in HII Database
408 //
409 DeleteString (HiiValue->Value.string, Selection->FormSet->HiiHandle);
410 }
411
412 if (!EFI_ERROR (Status)) {
413 switch (ActionRequest) {
414 case EFI_BROWSER_ACTION_REQUEST_RESET:
415 gResetRequired = TRUE;
416 break;
417
418 case EFI_BROWSER_ACTION_REQUEST_SUBMIT:
419 SubmitForm (Selection->FormSet, Selection->Form);
420 break;
421
422 case EFI_BROWSER_ACTION_REQUEST_EXIT:
423 Selection->Action = UI_ACTION_EXIT;
424 gNvUpdateRequired = FALSE;
425 break;
426
427 default:
428 break;
429 }
430 }
431 }
432
433 MenuRefreshEntry = MenuRefreshEntry->Next;
434
435 } while (MenuRefreshEntry != NULL);
436
437 if (mHiiPackageListUpdated) {
438 //
439 // Package list is updated, force to reparse IFR binary of target Formset
440 //
441 mHiiPackageListUpdated = FALSE;
442 Selection->Action = UI_ACTION_REFRESH_FORMSET;
443 return EFI_SUCCESS;
444 }
445 }
446
447 return EFI_TIMEOUT;
448 }
449
450
451 /**
452 Wait for a given event to fire, or for an optional timeout to expire.
453
454 @param Event The event to wait for
455 @param Timeout An optional timeout value in 100 ns units.
456 @param RefreshInterval Menu refresh interval (in seconds).
457
458 @retval EFI_SUCCESS Event fired before Timeout expired.
459 @retval EFI_TIME_OUT Timout expired before Event fired.
460
461 **/
462 EFI_STATUS
463 UiWaitForSingleEvent (
464 IN EFI_EVENT Event,
465 IN UINT64 Timeout, OPTIONAL
466 IN UINT8 RefreshInterval OPTIONAL
467 )
468 {
469 EFI_STATUS Status;
470 UINTN Index;
471 EFI_EVENT TimerEvent;
472 EFI_EVENT WaitList[2];
473
474 if (Timeout != 0) {
475 //
476 // Create a timer event
477 //
478 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
479 if (!EFI_ERROR (Status)) {
480 //
481 // Set the timer event
482 //
483 gBS->SetTimer (
484 TimerEvent,
485 TimerRelative,
486 Timeout
487 );
488
489 //
490 // Wait for the original event or the timer
491 //
492 WaitList[0] = Event;
493 WaitList[1] = TimerEvent;
494 Status = gBS->WaitForEvent (2, WaitList, &Index);
495 gBS->CloseEvent (TimerEvent);
496
497 //
498 // If the timer expired, change the return to timed out
499 //
500 if (!EFI_ERROR (Status) && Index == 1) {
501 Status = EFI_TIMEOUT;
502 }
503 }
504 } else {
505 //
506 // Update screen every second
507 //
508 if (RefreshInterval == 0) {
509 Timeout = ONE_SECOND;
510 } else {
511 Timeout = RefreshInterval * ONE_SECOND;
512 }
513
514 do {
515 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
516
517 //
518 // Set the timer event
519 //
520 gBS->SetTimer (
521 TimerEvent,
522 TimerRelative,
523 Timeout
524 );
525
526 //
527 // Wait for the original event or the timer
528 //
529 WaitList[0] = Event;
530 WaitList[1] = TimerEvent;
531 Status = gBS->WaitForEvent (2, WaitList, &Index);
532
533 //
534 // If the timer expired, update anything that needs a refresh and keep waiting
535 //
536 if (!EFI_ERROR (Status) && Index == 1) {
537 Status = EFI_TIMEOUT;
538 if (RefreshInterval != 0) {
539 Status = RefreshForm ();
540 }
541 }
542
543 gBS->CloseEvent (TimerEvent);
544 } while (Status == EFI_TIMEOUT);
545 }
546
547 return Status;
548 }
549
550
551 /**
552 Add one menu option by specified description and context.
553
554 @param String String description for this option.
555 @param Handle Hii handle for the package list.
556 @param Statement Statement of this Menu Option.
557 @param NumberOfLines Display lines for this Menu Option.
558 @param MenuItemCount The index for this Option in the Menu.
559
560 @retval Pointer Pointer to the added Menu Option.
561
562 **/
563 UI_MENU_OPTION *
564 UiAddMenuOption (
565 IN CHAR16 *String,
566 IN EFI_HII_HANDLE Handle,
567 IN FORM_BROWSER_STATEMENT *Statement,
568 IN UINT16 NumberOfLines,
569 IN UINT16 MenuItemCount
570 )
571 {
572 UI_MENU_OPTION *MenuOption;
573 UINTN Index;
574 UINTN Count;
575
576 Count = 1;
577 MenuOption = NULL;
578
579 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
580 //
581 // Add three MenuOptions for Date/Time
582 // Data format : [01/02/2004] [11:22:33]
583 // Line number : 0 0 1 0 0 1
584 //
585 NumberOfLines = 0;
586 Count = 3;
587
588 if (Statement->Storage == NULL) {
589 //
590 // For RTC type of date/time, set default refresh interval to be 1 second
591 //
592 if (Statement->RefreshInterval == 0) {
593 Statement->RefreshInterval = 1;
594 }
595 }
596 }
597
598 for (Index = 0; Index < Count; Index++) {
599 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
600 ASSERT (MenuOption);
601
602 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
603 MenuOption->Description = String;
604 MenuOption->Handle = Handle;
605 MenuOption->ThisTag = Statement;
606 MenuOption->EntryNumber = MenuItemCount;
607
608 if (Index == 2) {
609 //
610 // Override LineNumber for the MenuOption in Date/Time sequence
611 //
612 MenuOption->Skip = 1;
613 } else {
614 MenuOption->Skip = NumberOfLines;
615 }
616 MenuOption->Sequence = Index;
617
618 if (Statement->GrayOutExpression != NULL) {
619 MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b;
620 }
621
622 switch (Statement->Operand) {
623 case EFI_IFR_ORDERED_LIST_OP:
624 case EFI_IFR_ONE_OF_OP:
625 case EFI_IFR_NUMERIC_OP:
626 case EFI_IFR_TIME_OP:
627 case EFI_IFR_DATE_OP:
628 case EFI_IFR_CHECKBOX_OP:
629 case EFI_IFR_PASSWORD_OP:
630 case EFI_IFR_STRING_OP:
631 //
632 // User could change the value of these items
633 //
634 MenuOption->IsQuestion = TRUE;
635 break;
636
637 default:
638 MenuOption->IsQuestion = FALSE;
639 break;
640 }
641
642 if ((Statement->ValueExpression != NULL) ||
643 ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) {
644 MenuOption->ReadOnly = TRUE;
645 }
646
647 InsertTailList (&gMenuOption, &MenuOption->Link);
648 }
649
650 return MenuOption;
651 }
652
653
654 /**
655 Routine used to abstract a generic dialog interface and return the selected key or string
656
657 @param NumberOfLines The number of lines for the dialog box
658 @param HotKey Defines whether a single character is parsed
659 (TRUE) and returned in KeyValue or a string is
660 returned in StringBuffer. Two special characters
661 are considered when entering a string, a SCAN_ESC
662 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
663 string input and returns
664 @param MaximumStringSize The maximum size in bytes of a typed in string
665 (each character is a CHAR16) and the minimum
666 string returned is two bytes
667 @param StringBuffer The passed in pointer to the buffer which will
668 hold the typed in string if HotKey is FALSE
669 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
670 @param ... A series of (quantity == NumberOfLines) text
671 strings which will be used to construct the dialog
672 box
673
674 @retval EFI_SUCCESS Displayed dialog and received user interaction
675 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
676 (StringBuffer == NULL) && (HotKey == FALSE))
677 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
678
679 **/
680 EFI_STATUS
681 EFIAPI
682 CreateDialog (
683 IN UINTN NumberOfLines,
684 IN BOOLEAN HotKey,
685 IN UINTN MaximumStringSize,
686 OUT CHAR16 *StringBuffer,
687 OUT EFI_INPUT_KEY *KeyValue,
688 ...
689 )
690 {
691 VA_LIST Marker;
692 UINTN Count;
693 EFI_INPUT_KEY Key;
694 UINTN LargestString;
695 CHAR16 *TempString;
696 CHAR16 *BufferedString;
697 CHAR16 *StackString;
698 CHAR16 KeyPad[2];
699 UINTN Start;
700 UINTN Top;
701 UINTN Index;
702 EFI_STATUS Status;
703 BOOLEAN SelectionComplete;
704 UINTN InputOffset;
705 UINTN CurrentAttribute;
706 UINTN DimensionsWidth;
707 UINTN DimensionsHeight;
708
709 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
710 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
711
712 SelectionComplete = FALSE;
713 InputOffset = 0;
714 TempString = AllocateZeroPool (MaximumStringSize * 2);
715 BufferedString = AllocateZeroPool (MaximumStringSize * 2);
716 CurrentAttribute = gST->ConOut->Mode->Attribute;
717
718 ASSERT (TempString);
719 ASSERT (BufferedString);
720
721 VA_START (Marker, KeyValue);
722
723 //
724 // Zero the outgoing buffer
725 //
726 ZeroMem (StringBuffer, MaximumStringSize);
727
728 if (HotKey) {
729 if (KeyValue == NULL) {
730 return EFI_INVALID_PARAMETER;
731 }
732 } else {
733 if (StringBuffer == NULL) {
734 return EFI_INVALID_PARAMETER;
735 }
736 }
737 //
738 // Disable cursor
739 //
740 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
741
742 LargestString = 0;
743
744 //
745 // Determine the largest string in the dialog box
746 // Notice we are starting with 1 since String is the first string
747 //
748 for (Count = 0; Count < NumberOfLines; Count++) {
749 StackString = VA_ARG (Marker, CHAR16 *);
750
751 if (StackString[0] == L' ') {
752 InputOffset = Count + 1;
753 }
754
755 if ((GetStringWidth (StackString) / 2) > LargestString) {
756 //
757 // Size of the string visually and subtract the width by one for the null-terminator
758 //
759 LargestString = (GetStringWidth (StackString) / 2);
760 }
761 }
762 VA_END (Marker);
763
764 Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
765 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
766
767 Count = 0;
768
769 //
770 // Display the Popup
771 //
772 VA_START (Marker, KeyValue);
773 CreateSharedPopUp (LargestString, NumberOfLines, Marker);
774 VA_END (Marker);
775
776 //
777 // Take the first key typed and report it back?
778 //
779 if (HotKey) {
780 Status = WaitForKeyStroke (&Key);
781 ASSERT_EFI_ERROR (Status);
782 CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
783
784 } else {
785 do {
786 Status = WaitForKeyStroke (&Key);
787
788 switch (Key.UnicodeChar) {
789 case CHAR_NULL:
790 switch (Key.ScanCode) {
791 case SCAN_ESC:
792 FreePool (TempString);
793 FreePool (BufferedString);
794 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
795 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
796 return EFI_DEVICE_ERROR;
797
798 default:
799 break;
800 }
801
802 break;
803
804 case CHAR_CARRIAGE_RETURN:
805 SelectionComplete = TRUE;
806 FreePool (TempString);
807 FreePool (BufferedString);
808 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
809 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
810 return EFI_SUCCESS;
811 break;
812
813 case CHAR_BACKSPACE:
814 if (StringBuffer[0] != CHAR_NULL) {
815 for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
816 TempString[Index] = StringBuffer[Index];
817 }
818 //
819 // Effectively truncate string by 1 character
820 //
821 TempString[Index - 1] = CHAR_NULL;
822 StrCpy (StringBuffer, TempString);
823 }
824
825 default:
826 //
827 // If it is the beginning of the string, don't worry about checking maximum limits
828 //
829 if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
830 StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
831 StrnCpy (TempString, &Key.UnicodeChar, 1);
832 } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
833 KeyPad[0] = Key.UnicodeChar;
834 KeyPad[1] = CHAR_NULL;
835 StrCat (StringBuffer, KeyPad);
836 StrCat (TempString, KeyPad);
837 }
838 //
839 // If the width of the input string is now larger than the screen, we nee to
840 // adjust the index to start printing portions of the string
841 //
842 SetUnicodeMem (BufferedString, LargestString, L' ');
843
844 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
845
846 if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
847 Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
848 } else {
849 Index = 0;
850 }
851
852 for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
853 BufferedString[Count] = StringBuffer[Index];
854 }
855
856 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
857 break;
858 }
859 } while (!SelectionComplete);
860 }
861
862 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
863 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
864 return EFI_SUCCESS;
865 }
866
867 /**
868 Draw a pop up windows based on the dimension, number of lines and
869 strings specified.
870
871 @param RequestedWidth The width of the pop-up.
872 @param NumberOfLines The number of lines.
873 @param Marker The variable argument list for the list of string to be printed.
874
875 **/
876 VOID
877 CreateSharedPopUp (
878 IN UINTN RequestedWidth,
879 IN UINTN NumberOfLines,
880 IN VA_LIST Marker
881 )
882 {
883 UINTN Index;
884 UINTN Count;
885 CHAR16 Character;
886 UINTN Start;
887 UINTN End;
888 UINTN Top;
889 UINTN Bottom;
890 CHAR16 *String;
891 UINTN DimensionsWidth;
892 UINTN DimensionsHeight;
893
894 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
895 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
896
897 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
898
899 if ((RequestedWidth + 2) > DimensionsWidth) {
900 RequestedWidth = DimensionsWidth - 2;
901 }
902
903 //
904 // Subtract the PopUp width from total Columns, allow for one space extra on
905 // each end plus a border.
906 //
907 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
908 End = Start + RequestedWidth + 1;
909
910 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
911 Bottom = Top + NumberOfLines + 2;
912
913 Character = BOXDRAW_DOWN_RIGHT;
914 PrintCharAt (Start, Top, Character);
915 Character = BOXDRAW_HORIZONTAL;
916 for (Index = Start; Index + 2 < End; Index++) {
917 PrintChar (Character);
918 }
919
920 Character = BOXDRAW_DOWN_LEFT;
921 PrintChar (Character);
922 Character = BOXDRAW_VERTICAL;
923
924 Count = 0;
925 for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
926 String = VA_ARG (Marker, CHAR16*);
927
928 //
929 // This will clear the background of the line - we never know who might have been
930 // here before us. This differs from the next clear in that it used the non-reverse
931 // video for normal printing.
932 //
933 if (GetStringWidth (String) / 2 > 1) {
934 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
935 }
936
937 //
938 // Passing in a space results in the assumption that this is where typing will occur
939 //
940 if (String[0] == L' ') {
941 ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
942 }
943
944 //
945 // Passing in a NULL results in a blank space
946 //
947 if (String[0] == CHAR_NULL) {
948 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
949 }
950
951 PrintStringAt (
952 ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
953 Index + 1,
954 String
955 );
956 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
957 PrintCharAt (Start, Index + 1, Character);
958 PrintCharAt (End - 1, Index + 1, Character);
959 }
960
961 Character = BOXDRAW_UP_RIGHT;
962 PrintCharAt (Start, Bottom - 1, Character);
963 Character = BOXDRAW_HORIZONTAL;
964 for (Index = Start; Index + 2 < End; Index++) {
965 PrintChar (Character);
966 }
967
968 Character = BOXDRAW_UP_LEFT;
969 PrintChar (Character);
970 }
971
972 /**
973 Draw a pop up windows based on the dimension, number of lines and
974 strings specified.
975
976 @param RequestedWidth The width of the pop-up.
977 @param NumberOfLines The number of lines.
978 @param ... A series of text strings that displayed in the pop-up.
979
980 **/
981 VOID
982 EFIAPI
983 CreateMultiStringPopUp (
984 IN UINTN RequestedWidth,
985 IN UINTN NumberOfLines,
986 ...
987 )
988 {
989 VA_LIST Marker;
990
991 VA_START (Marker, NumberOfLines);
992
993 CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
994
995 VA_END (Marker);
996 }
997
998
999 /**
1000 Update status bar on the bottom of menu.
1001
1002 @param MessageType The type of message to be shown.
1003 @param Flags The flags in Question header.
1004 @param State Set or clear.
1005
1006 **/
1007 VOID
1008 UpdateStatusBar (
1009 IN UINTN MessageType,
1010 IN UINT8 Flags,
1011 IN BOOLEAN State
1012 )
1013 {
1014 UINTN Index;
1015 CHAR16 *NvUpdateMessage;
1016 CHAR16 *InputErrorMessage;
1017
1018 NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
1019 InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
1020
1021 switch (MessageType) {
1022 case INPUT_ERROR:
1023 if (State) {
1024 gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
1025 PrintStringAt (
1026 gScreenDimensions.LeftColumn + gPromptBlockWidth,
1027 gScreenDimensions.BottomRow - 1,
1028 InputErrorMessage
1029 );
1030 mInputError = TRUE;
1031 } else {
1032 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
1033 for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
1034 PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" ");
1035 }
1036
1037 mInputError = FALSE;
1038 }
1039 break;
1040
1041 case NV_UPDATE_REQUIRED:
1042 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
1043 if (State) {
1044 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
1045 PrintStringAt (
1046 gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
1047 gScreenDimensions.BottomRow - 1,
1048 NvUpdateMessage
1049 );
1050 gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
1051
1052 gNvUpdateRequired = TRUE;
1053 } else {
1054 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
1055 for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
1056 PrintAt (
1057 (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
1058 gScreenDimensions.BottomRow - 1,
1059 L" "
1060 );
1061 }
1062
1063 gNvUpdateRequired = FALSE;
1064 }
1065 }
1066 break;
1067
1068 case REFRESH_STATUS_BAR:
1069 if (mInputError) {
1070 UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
1071 }
1072
1073 if (gNvUpdateRequired) {
1074 UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
1075 }
1076 break;
1077
1078 default:
1079 break;
1080 }
1081
1082 FreePool (InputErrorMessage);
1083 FreePool (NvUpdateMessage);
1084 return ;
1085 }
1086
1087
1088 /**
1089 Get the supported width for a particular op-code
1090
1091 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1092 @param Handle The handle in the HII database being used
1093
1094 @return Returns the number of CHAR16 characters that is support.
1095
1096 **/
1097 UINT16
1098 GetWidth (
1099 IN FORM_BROWSER_STATEMENT *Statement,
1100 IN EFI_HII_HANDLE Handle
1101 )
1102 {
1103 CHAR16 *String;
1104 UINTN Size;
1105 UINT16 Width;
1106
1107 Size = 0;
1108
1109 //
1110 // See if the second text parameter is really NULL
1111 //
1112 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1113 String = GetToken (Statement->TextTwo, Handle);
1114 Size = StrLen (String);
1115 FreePool (String);
1116 }
1117
1118 if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
1119 (Statement->Operand == EFI_IFR_REF_OP) ||
1120 (Statement->Operand == EFI_IFR_PASSWORD_OP) ||
1121 (Statement->Operand == EFI_IFR_ACTION_OP) ||
1122 (Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
1123 //
1124 // Allow a wide display if text op-code and no secondary text op-code
1125 //
1126 ((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
1127 ) {
1128 Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
1129 } else {
1130 Width = (UINT16) gPromptBlockWidth;
1131 }
1132
1133 if (Statement->InSubtitle) {
1134 Width -= SUBTITLE_INDENT;
1135 }
1136
1137 return (UINT16) (Width - LEFT_SKIPPED_COLUMNS);
1138 }
1139
1140 /**
1141 Will copy LineWidth amount of a string in the OutputString buffer and return the
1142 number of CHAR16 characters that were copied into the OutputString buffer.
1143
1144 @param InputString String description for this option.
1145 @param LineWidth Width of the desired string to extract in CHAR16
1146 characters
1147 @param Index Where in InputString to start the copy process
1148 @param OutputString Buffer to copy the string into
1149
1150 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1151
1152 **/
1153 UINT16
1154 GetLineByWidth (
1155 IN CHAR16 *InputString,
1156 IN UINT16 LineWidth,
1157 IN OUT UINTN *Index,
1158 OUT CHAR16 **OutputString
1159 )
1160 {
1161 UINT16 Count;
1162 UINT16 Count2;
1163
1164 if (GetLineByWidthFinished) {
1165 GetLineByWidthFinished = FALSE;
1166 return (UINT16) 0;
1167 }
1168
1169 Count = LineWidth;
1170 Count2 = 0;
1171
1172 *OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
1173
1174 //
1175 // Ensure we have got a valid buffer
1176 //
1177 if (*OutputString != NULL) {
1178
1179 //
1180 //NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen.
1181 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1182 //
1183 if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
1184 *Index = *Index + 2;
1185 }
1186
1187 //
1188 // Fast-forward the string and see if there is a carriage-return in the string
1189 //
1190 for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
1191 ;
1192
1193 //
1194 // Copy the desired LineWidth of data to the output buffer.
1195 // Also make sure that we don't copy more than the string.
1196 // Also make sure that if there are linefeeds, we account for them.
1197 //
1198 if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
1199 (StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
1200 ) {
1201 //
1202 // Convert to CHAR16 value and show that we are done with this operation
1203 //
1204 LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
1205 if (LineWidth != 0) {
1206 GetLineByWidthFinished = TRUE;
1207 }
1208 } else {
1209 if (Count2 == LineWidth) {
1210 //
1211 // Rewind the string from the maximum size until we see a space to break the line
1212 //
1213 for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
1214 ;
1215 if (LineWidth == 0) {
1216 LineWidth = Count;
1217 }
1218 } else {
1219 LineWidth = Count2;
1220 }
1221 }
1222
1223 CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
1224
1225 //
1226 // If currently pointing to a space, increment the index to the first non-space character
1227 //
1228 for (;
1229 (InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
1230 (*Index)++
1231 )
1232 ;
1233 *Index = (UINT16) (*Index + LineWidth);
1234 return LineWidth;
1235 } else {
1236 return (UINT16) 0;
1237 }
1238 }
1239
1240
1241 /**
1242 Update display lines for a Menu Option.
1243
1244 @param Selection The user's selection.
1245 @param MenuOption The MenuOption to be checked.
1246 @param OptionalString The option string.
1247 @param SkipValue The number of lins to skip.
1248
1249 **/
1250 VOID
1251 UpdateOptionSkipLines (
1252 IN UI_MENU_SELECTION *Selection,
1253 IN UI_MENU_OPTION *MenuOption,
1254 OUT CHAR16 **OptionalString,
1255 IN UINTN SkipValue
1256 )
1257 {
1258 UINTN Index;
1259 UINT16 Width;
1260 UINTN Row;
1261 UINTN OriginalRow;
1262 CHAR16 *OutputString;
1263 CHAR16 *OptionString;
1264
1265 Row = 0;
1266 OptionString = *OptionalString;
1267 OutputString = NULL;
1268
1269 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1270
1271 if (OptionString != NULL) {
1272 Width = (UINT16) gOptionBlockWidth;
1273
1274 OriginalRow = Row;
1275
1276 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1277 //
1278 // If there is more string to process print on the next row and increment the Skip value
1279 //
1280 if (StrLen (&OptionString[Index]) != 0) {
1281 if (SkipValue == 0) {
1282 Row++;
1283 //
1284 // Since the Number of lines for this menu entry may or may not be reflected accurately
1285 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1286 // some testing to ensure we are keeping this in-sync.
1287 //
1288 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1289 //
1290 if ((Row - OriginalRow) >= MenuOption->Skip) {
1291 MenuOption->Skip++;
1292 }
1293 }
1294 }
1295
1296 FreePool (OutputString);
1297 if (SkipValue != 0) {
1298 SkipValue--;
1299 }
1300 }
1301
1302 Row = OriginalRow;
1303 }
1304
1305 *OptionalString = OptionString;
1306 }
1307
1308
1309 /**
1310 Check whether this Menu Option could be highlighted.
1311
1312 This is an internal function.
1313
1314 @param MenuOption The MenuOption to be checked.
1315
1316 @retval TRUE This Menu Option is selectable.
1317 @retval FALSE This Menu Option could not be selected.
1318
1319 **/
1320 BOOLEAN
1321 IsSelectable (
1322 UI_MENU_OPTION *MenuOption
1323 )
1324 {
1325 if ((MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) ||
1326 MenuOption->GrayOut || MenuOption->ReadOnly) {
1327 return FALSE;
1328 } else {
1329 return TRUE;
1330 }
1331 }
1332
1333
1334 /**
1335 Determine if the menu is the last menu that can be selected.
1336
1337 This is an internal function.
1338
1339 @param Direction The scroll direction. False is down. True is up.
1340 @param CurrentPos The current focus.
1341
1342 @return FALSE -- the menu isn't the last menu that can be selected.
1343 @return TRUE -- the menu is the last menu that can be selected.
1344
1345 **/
1346 BOOLEAN
1347 ValueIsScroll (
1348 IN BOOLEAN Direction,
1349 IN LIST_ENTRY *CurrentPos
1350 )
1351 {
1352 LIST_ENTRY *Temp;
1353 UI_MENU_OPTION *MenuOption;
1354
1355 Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
1356
1357 if (Temp == &gMenuOption) {
1358 return TRUE;
1359 }
1360
1361 for (; Temp != &gMenuOption; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
1362 MenuOption = MENU_OPTION_FROM_LINK (Temp);
1363 if (IsSelectable (MenuOption)) {
1364 return FALSE;
1365 }
1366 }
1367
1368 return TRUE;
1369 }
1370
1371
1372 /**
1373 Move to next selectable statement.
1374
1375 This is an internal function.
1376
1377 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1378 @param CurrentPosition Current position.
1379
1380 @return The row distance from current MenuOption to next selectable MenuOption.
1381
1382 **/
1383 INTN
1384 MoveToNextStatement (
1385 IN BOOLEAN GoUp,
1386 IN OUT LIST_ENTRY **CurrentPosition
1387 )
1388 {
1389 INTN Distance;
1390 LIST_ENTRY *Pos;
1391 BOOLEAN HitEnd;
1392 UI_MENU_OPTION *NextMenuOption;
1393
1394 Distance = 0;
1395 Pos = *CurrentPosition;
1396 HitEnd = FALSE;
1397
1398 while (TRUE) {
1399 NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1400 if (IsSelectable (NextMenuOption)) {
1401 break;
1402 }
1403 if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
1404 HitEnd = TRUE;
1405 break;
1406 }
1407 Distance += NextMenuOption->Skip;
1408 Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
1409 }
1410
1411 if (HitEnd) {
1412 //
1413 // If we hit end there is still no statement can be focused,
1414 // we go backwards to find the statement can be focused.
1415 //
1416 Distance = 0;
1417 Pos = *CurrentPosition;
1418
1419 while (TRUE) {
1420 NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1421 if (IsSelectable (NextMenuOption)) {
1422 break;
1423 }
1424 if ((!GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
1425 ASSERT (FALSE);
1426 break;
1427 }
1428 Distance -= NextMenuOption->Skip;
1429 Pos = (!GoUp ? Pos->BackLink : Pos->ForwardLink);
1430 }
1431 }
1432
1433 *CurrentPosition = &NextMenuOption->Link;
1434 return Distance;
1435 }
1436
1437
1438 /**
1439 Adjust Data and Time position accordingly.
1440 Data format : [01/02/2004] [11:22:33]
1441 Line number : 0 0 1 0 0 1
1442
1443 This is an internal function.
1444
1445 @param DirectionUp the up or down direction. False is down. True is
1446 up.
1447 @param CurrentPosition Current position. On return: Point to the last
1448 Option (Year or Second) if up; Point to the first
1449 Option (Month or Hour) if down.
1450
1451 @return Return line number to pad. It is possible that we stand on a zero-advance
1452 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1453
1454 **/
1455 UINTN
1456 AdjustDateAndTimePosition (
1457 IN BOOLEAN DirectionUp,
1458 IN OUT LIST_ENTRY **CurrentPosition
1459 )
1460 {
1461 UINTN Count;
1462 LIST_ENTRY *NewPosition;
1463 UI_MENU_OPTION *MenuOption;
1464 UINTN PadLineNumber;
1465
1466 PadLineNumber = 0;
1467 NewPosition = *CurrentPosition;
1468 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1469
1470 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
1471 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
1472 //
1473 // Calculate the distance from current position to the last Date/Time MenuOption
1474 //
1475 Count = 0;
1476 while (MenuOption->Skip == 0) {
1477 Count++;
1478 NewPosition = NewPosition->ForwardLink;
1479 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1480 PadLineNumber = 1;
1481 }
1482
1483 NewPosition = *CurrentPosition;
1484 if (DirectionUp) {
1485 //
1486 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1487 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1488 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1489 // checking can be done.
1490 //
1491 while (Count++ < 2) {
1492 NewPosition = NewPosition->BackLink;
1493 }
1494 } else {
1495 //
1496 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1497 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1498 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1499 // checking can be done.
1500 //
1501 while (Count-- > 0) {
1502 NewPosition = NewPosition->ForwardLink;
1503 }
1504 }
1505
1506 *CurrentPosition = NewPosition;
1507 }
1508
1509 return PadLineNumber;
1510 }
1511
1512 /**
1513 Find HII Handle in the HII database associated with given Device Path.
1514
1515 If DevicePath is NULL, then ASSERT.
1516
1517 @param DevicePath Device Path associated with the HII package list
1518 handle.
1519
1520 @retval Handle HII package list Handle associated with the Device
1521 Path.
1522 @retval NULL Hii Package list handle is not found.
1523
1524 **/
1525 EFI_HII_HANDLE
1526 EFIAPI
1527 DevicePathToHiiHandle (
1528 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1529 )
1530 {
1531 EFI_STATUS Status;
1532 EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
1533 UINTN BufferSize;
1534 UINTN HandleCount;
1535 UINTN Index;
1536 EFI_HANDLE Handle;
1537 EFI_HANDLE DriverHandle;
1538 EFI_HII_HANDLE *HiiHandles;
1539 EFI_HII_HANDLE HiiHandle;
1540
1541 ASSERT (DevicePath != NULL);
1542
1543 TmpDevicePath = DevicePath;
1544 //
1545 // Locate Device Path Protocol handle buffer
1546 //
1547 Status = gBS->LocateDevicePath (
1548 &gEfiDevicePathProtocolGuid,
1549 &TmpDevicePath,
1550 &DriverHandle
1551 );
1552 if (EFI_ERROR (Status) || !IsDevicePathEnd (TmpDevicePath)) {
1553 return NULL;
1554 }
1555
1556 //
1557 // Retrieve all HII Handles from HII database
1558 //
1559 BufferSize = 0x1000;
1560 HiiHandles = AllocatePool (BufferSize);
1561 ASSERT (HiiHandles != NULL);
1562 Status = mHiiDatabase->ListPackageLists (
1563 mHiiDatabase,
1564 EFI_HII_PACKAGE_TYPE_ALL,
1565 NULL,
1566 &BufferSize,
1567 HiiHandles
1568 );
1569 if (Status == EFI_BUFFER_TOO_SMALL) {
1570 FreePool (HiiHandles);
1571 HiiHandles = AllocatePool (BufferSize);
1572 ASSERT (HiiHandles != NULL);
1573
1574 Status = mHiiDatabase->ListPackageLists (
1575 mHiiDatabase,
1576 EFI_HII_PACKAGE_TYPE_ALL,
1577 NULL,
1578 &BufferSize,
1579 HiiHandles
1580 );
1581 }
1582
1583 if (EFI_ERROR (Status)) {
1584 FreePool (HiiHandles);
1585 return NULL;
1586 }
1587
1588 //
1589 // Search Hii Handle by Driver Handle
1590 //
1591 HiiHandle = NULL;
1592 HandleCount = BufferSize / sizeof (EFI_HII_HANDLE);
1593 for (Index = 0; Index < HandleCount; Index++) {
1594 Status = mHiiDatabase->GetPackageListHandle (
1595 mHiiDatabase,
1596 HiiHandles[Index],
1597 &Handle
1598 );
1599 if (!EFI_ERROR (Status) && (Handle == DriverHandle)) {
1600 HiiHandle = HiiHandles[Index];
1601 break;
1602 }
1603 }
1604
1605 FreePool (HiiHandles);
1606 return HiiHandle;
1607 }
1608
1609 /**
1610 Display menu and wait for user to select one menu option, then return it.
1611 If AutoBoot is enabled, then if user doesn't select any option,
1612 after period of time, it will automatically return the first menu option.
1613
1614 @param Selection Menu selection.
1615
1616 @retval EFI_SUCESSS This function always return successfully for now.
1617
1618 **/
1619 EFI_STATUS
1620 UiDisplayMenu (
1621 IN OUT UI_MENU_SELECTION *Selection
1622 )
1623 {
1624 INTN SkipValue;
1625 INTN Difference;
1626 INTN OldSkipValue;
1627 UINTN DistanceValue;
1628 UINTN Row;
1629 UINTN Col;
1630 UINTN Temp;
1631 UINTN Temp2;
1632 UINTN TopRow;
1633 UINTN BottomRow;
1634 UINTN OriginalRow;
1635 UINTN Index;
1636 UINT32 Count;
1637 UINT16 Width;
1638 CHAR16 *StringPtr;
1639 CHAR16 *OptionString;
1640 CHAR16 *OutputString;
1641 CHAR16 *FormattedString;
1642 CHAR16 YesResponse;
1643 CHAR16 NoResponse;
1644 BOOLEAN NewLine;
1645 BOOLEAN Repaint;
1646 BOOLEAN SavedValue;
1647 BOOLEAN UpArrow;
1648 BOOLEAN DownArrow;
1649 EFI_STATUS Status;
1650 EFI_INPUT_KEY Key;
1651 LIST_ENTRY *Link;
1652 LIST_ENTRY *NewPos;
1653 LIST_ENTRY *TopOfScreen;
1654 LIST_ENTRY *SavedListEntry;
1655 UI_MENU_OPTION *MenuOption;
1656 UI_MENU_OPTION *NextMenuOption;
1657 UI_MENU_OPTION *SavedMenuOption;
1658 UI_MENU_OPTION *PreviousMenuOption;
1659 UI_CONTROL_FLAG ControlFlag;
1660 EFI_SCREEN_DESCRIPTOR LocalScreen;
1661 MENU_REFRESH_ENTRY *MenuRefreshEntry;
1662 UI_SCREEN_OPERATION ScreenOperation;
1663 UINT8 MinRefreshInterval;
1664 UINTN BufferSize;
1665 UINT16 DefaultId;
1666 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1667 FORM_BROWSER_STATEMENT *Statement;
1668 CHAR16 TemStr[2];
1669 UINT8 *DevicePathBuffer;
1670 UINT8 DigitUint8;
1671 UI_MENU_LIST *CurrentMenu;
1672 UI_MENU_LIST *MenuList;
1673 FORM_BROWSER_FORM *RefForm;
1674
1675 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
1676
1677 Status = EFI_SUCCESS;
1678 FormattedString = NULL;
1679 OptionString = NULL;
1680 ScreenOperation = UiNoOperation;
1681 NewLine = TRUE;
1682 MinRefreshInterval = 0;
1683 DefaultId = 0;
1684
1685 OutputString = NULL;
1686 UpArrow = FALSE;
1687 DownArrow = FALSE;
1688 SkipValue = 0;
1689 OldSkipValue = 0;
1690 MenuRefreshEntry = gMenuRefreshHead;
1691
1692 NextMenuOption = NULL;
1693 PreviousMenuOption = NULL;
1694 SavedMenuOption = NULL;
1695 RefForm = NULL;
1696
1697 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
1698
1699 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE){
1700 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1701 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1702 } else {
1703 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1704 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1705 }
1706
1707 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
1708 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
1709
1710 Selection->TopRow = TopRow;
1711 Selection->BottomRow = BottomRow;
1712 Selection->PromptCol = Col;
1713 Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1714 Selection->Statement = NULL;
1715
1716 TopOfScreen = gMenuOption.ForwardLink;
1717 Repaint = TRUE;
1718 MenuOption = NULL;
1719
1720 //
1721 // Find current Menu
1722 //
1723 CurrentMenu = UiFindMenuList (&Selection->FormSetGuid, Selection->FormId);
1724 if (CurrentMenu == NULL) {
1725 //
1726 // Current menu not found, add it to the menu tree
1727 //
1728 CurrentMenu = UiAddMenuList (NULL, &Selection->FormSetGuid, Selection->FormId);
1729 }
1730 ASSERT (CurrentMenu != NULL);
1731
1732 if (Selection->QuestionId == 0) {
1733 //
1734 // Highlight not specified, fetch it from cached menu
1735 //
1736 Selection->QuestionId = CurrentMenu->QuestionId;
1737 }
1738
1739 //
1740 // Get user's selection
1741 //
1742 NewPos = gMenuOption.ForwardLink;
1743
1744 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
1745 UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
1746
1747 ControlFlag = CfInitialization;
1748 Selection->Action = UI_ACTION_NONE;
1749 while (TRUE) {
1750 switch (ControlFlag) {
1751 case CfInitialization:
1752 if (IsListEmpty (&gMenuOption)) {
1753 ControlFlag = CfReadKey;
1754 } else {
1755 ControlFlag = CfCheckSelection;
1756 }
1757 break;
1758
1759 case CfCheckSelection:
1760 if (Selection->Action != UI_ACTION_NONE) {
1761 ControlFlag = CfExit;
1762 } else {
1763 ControlFlag = CfRepaint;
1764 }
1765 break;
1766
1767 case CfRepaint:
1768 ControlFlag = CfRefreshHighLight;
1769
1770 if (Repaint) {
1771 //
1772 // Display menu
1773 //
1774 DownArrow = FALSE;
1775 UpArrow = FALSE;
1776 Row = TopRow;
1777
1778 Temp = (UINTN) SkipValue;
1779 Temp2 = (UINTN) SkipValue;
1780
1781 ClearLines (
1782 LocalScreen.LeftColumn,
1783 LocalScreen.RightColumn,
1784 TopRow - SCROLL_ARROW_HEIGHT,
1785 BottomRow + SCROLL_ARROW_HEIGHT,
1786 FIELD_TEXT | FIELD_BACKGROUND
1787 );
1788
1789 UiFreeRefreshList ();
1790 MinRefreshInterval = 0;
1791
1792 for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
1793 MenuOption = MENU_OPTION_FROM_LINK (Link);
1794 MenuOption->Row = Row;
1795 MenuOption->Col = Col;
1796 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1797
1798 Statement = MenuOption->ThisTag;
1799 if (Statement->InSubtitle) {
1800 MenuOption->Col += SUBTITLE_INDENT;
1801 }
1802
1803 if (MenuOption->GrayOut) {
1804 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1805 } else {
1806 if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
1807 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1808 }
1809 }
1810
1811 Width = GetWidth (Statement, MenuOption->Handle);
1812 OriginalRow = Row;
1813
1814 if (Statement->Operand == EFI_IFR_REF_OP && ((gClassOfVfr & FORMSET_CLASS_PLATFORM_SETUP) == FORMSET_CLASS_PLATFORM_SETUP)) {
1815 //
1816 // Print Arrow for Goto button.
1817 //
1818 PrintAt (
1819 MenuOption->Col - LEFT_SKIPPED_COLUMNS,
1820 Row,
1821 L"%c",
1822 GEOMETRICSHAPE_RIGHT_TRIANGLE
1823 );
1824 }
1825
1826 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1827 if ((Temp == 0) && (Row <= BottomRow)) {
1828 PrintStringAt (MenuOption->Col, Row, OutputString);
1829 }
1830 //
1831 // If there is more string to process print on the next row and increment the Skip value
1832 //
1833 if (StrLen (&MenuOption->Description[Index]) != 0) {
1834 if (Temp == 0) {
1835 Row++;
1836 }
1837 }
1838
1839 FreePool (OutputString);
1840 if (Temp != 0) {
1841 Temp--;
1842 }
1843 }
1844
1845 Temp = 0;
1846 Row = OriginalRow;
1847
1848 Status = ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1849 if (EFI_ERROR (Status)) {
1850 //
1851 // Repaint to clear possible error prompt pop-up
1852 //
1853 Repaint = TRUE;
1854 NewLine = TRUE;
1855 ControlFlag = CfRepaint;
1856 break;
1857 }
1858
1859 if (OptionString != NULL) {
1860 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
1861 //
1862 // If leading spaces on OptionString - remove the spaces
1863 //
1864 for (Index = 0; OptionString[Index] == L' '; Index++) {
1865 MenuOption->OptCol++;
1866 }
1867
1868 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1869 OptionString[Count] = OptionString[Index];
1870 Count++;
1871 }
1872
1873 OptionString[Count] = CHAR_NULL;
1874 }
1875
1876 //
1877 // If Question request refresh, register the op-code
1878 //
1879 if (Statement->RefreshInterval != 0) {
1880 //
1881 // Menu will be refreshed at minimal interval of all Questions
1882 // which have refresh request
1883 //
1884 if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
1885 MinRefreshInterval = Statement->RefreshInterval;
1886 }
1887
1888 if (gMenuRefreshHead == NULL) {
1889 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1890 ASSERT (MenuRefreshEntry != NULL);
1891 MenuRefreshEntry->MenuOption = MenuOption;
1892 MenuRefreshEntry->Selection = Selection;
1893 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1894 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1895 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1896 gMenuRefreshHead = MenuRefreshEntry;
1897 } else {
1898 //
1899 // Advance to the last entry
1900 //
1901 for (MenuRefreshEntry = gMenuRefreshHead;
1902 MenuRefreshEntry->Next != NULL;
1903 MenuRefreshEntry = MenuRefreshEntry->Next
1904 )
1905 ;
1906 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1907 ASSERT (MenuRefreshEntry->Next != NULL);
1908 MenuRefreshEntry = MenuRefreshEntry->Next;
1909 MenuRefreshEntry->MenuOption = MenuOption;
1910 MenuRefreshEntry->Selection = Selection;
1911 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1912 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1913 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1914 }
1915 }
1916
1917 Width = (UINT16) gOptionBlockWidth;
1918 OriginalRow = Row;
1919
1920 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1921 if ((Temp2 == 0) && (Row <= BottomRow)) {
1922 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1923 }
1924 //
1925 // If there is more string to process print on the next row and increment the Skip value
1926 //
1927 if (StrLen (&OptionString[Index]) != 0) {
1928 if (Temp2 == 0) {
1929 Row++;
1930 //
1931 // Since the Number of lines for this menu entry may or may not be reflected accurately
1932 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1933 // some testing to ensure we are keeping this in-sync.
1934 //
1935 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1936 //
1937 if ((Row - OriginalRow) >= MenuOption->Skip) {
1938 MenuOption->Skip++;
1939 }
1940 }
1941 }
1942
1943 FreePool (OutputString);
1944 if (Temp2 != 0) {
1945 Temp2--;
1946 }
1947 }
1948
1949 Temp2 = 0;
1950 Row = OriginalRow;
1951
1952 FreePool (OptionString);
1953 }
1954 //
1955 // If this is a text op with secondary text information
1956 //
1957 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1958 StringPtr = GetToken (Statement->TextTwo, MenuOption->Handle);
1959
1960 Width = (UINT16) gOptionBlockWidth;
1961 OriginalRow = Row;
1962
1963 for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
1964 if ((Temp == 0) && (Row <= BottomRow)) {
1965 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1966 }
1967 //
1968 // If there is more string to process print on the next row and increment the Skip value
1969 //
1970 if (StrLen (&StringPtr[Index]) != 0) {
1971 if (Temp2 == 0) {
1972 Row++;
1973 //
1974 // Since the Number of lines for this menu entry may or may not be reflected accurately
1975 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1976 // some testing to ensure we are keeping this in-sync.
1977 //
1978 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1979 //
1980 if ((Row - OriginalRow) >= MenuOption->Skip) {
1981 MenuOption->Skip++;
1982 }
1983 }
1984 }
1985
1986 FreePool (OutputString);
1987 if (Temp2 != 0) {
1988 Temp2--;
1989 }
1990 }
1991
1992 Row = OriginalRow;
1993 FreePool (StringPtr);
1994 }
1995 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1996
1997 //
1998 // Need to handle the bottom of the display
1999 //
2000 if (MenuOption->Skip > 1) {
2001 Row += MenuOption->Skip - SkipValue;
2002 SkipValue = 0;
2003 } else {
2004 Row += MenuOption->Skip;
2005 }
2006
2007 if (Row > BottomRow) {
2008 if (!ValueIsScroll (FALSE, Link)) {
2009 DownArrow = TRUE;
2010 }
2011
2012 Row = BottomRow + 1;
2013 break;
2014 }
2015 }
2016
2017 if (!ValueIsScroll (TRUE, TopOfScreen)) {
2018 UpArrow = TRUE;
2019 }
2020
2021 if (UpArrow) {
2022 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2023 PrintAt (
2024 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2025 TopRow - SCROLL_ARROW_HEIGHT,
2026 L"%c",
2027 ARROW_UP
2028 );
2029 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2030 }
2031
2032 if (DownArrow) {
2033 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2034 PrintAt (
2035 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2036 BottomRow + SCROLL_ARROW_HEIGHT,
2037 L"%c",
2038 ARROW_DOWN
2039 );
2040 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2041 }
2042
2043 MenuOption = NULL;
2044 }
2045 break;
2046
2047 case CfRefreshHighLight:
2048 //
2049 // MenuOption: Last menu option that need to remove hilight
2050 // MenuOption is set to NULL in Repaint
2051 // NewPos: Current menu option that need to hilight
2052 //
2053 ControlFlag = CfUpdateHelpString;
2054
2055 //
2056 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2057 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2058 //
2059 SavedValue = Repaint;
2060 Repaint = FALSE;
2061
2062 if (Selection->QuestionId != 0) {
2063 NewPos = gMenuOption.ForwardLink;
2064 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2065
2066 while (SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId && NewPos->ForwardLink != &gMenuOption) {
2067 NewPos = NewPos->ForwardLink;
2068 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2069 }
2070 if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
2071 //
2072 // Target Question found, find its MenuOption
2073 //
2074 Link = TopOfScreen;
2075
2076 for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
2077 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2078 Index += SavedMenuOption->Skip;
2079 Link = Link->ForwardLink;
2080 }
2081
2082 if (Link != NewPos || Index > BottomRow) {
2083 //
2084 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2085 //
2086 Link = NewPos;
2087 for (Index = TopRow; Index <= BottomRow; ) {
2088 Link = Link->BackLink;
2089 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2090 Index += SavedMenuOption->Skip;
2091 }
2092 TopOfScreen = Link->ForwardLink;
2093
2094 Repaint = TRUE;
2095 NewLine = TRUE;
2096 ControlFlag = CfRepaint;
2097 break;
2098 }
2099 } else {
2100 //
2101 // Target Question not found, highlight the default menu option
2102 //
2103 NewPos = TopOfScreen;
2104 }
2105
2106 Selection->QuestionId = 0;
2107 }
2108
2109 if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
2110 if (MenuOption != NULL) {
2111 //
2112 // Remove highlight on last Menu Option
2113 //
2114 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2115 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2116 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2117 if (OptionString != NULL) {
2118 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2119 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
2120 ) {
2121 //
2122 // If leading spaces on OptionString - remove the spaces
2123 //
2124 for (Index = 0; OptionString[Index] == L' '; Index++)
2125 ;
2126
2127 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
2128 OptionString[Count] = OptionString[Index];
2129 Count++;
2130 }
2131
2132 OptionString[Count] = CHAR_NULL;
2133 }
2134
2135 Width = (UINT16) gOptionBlockWidth;
2136 OriginalRow = MenuOption->Row;
2137
2138 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
2139 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2140 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2141 }
2142 //
2143 // If there is more string to process print on the next row and increment the Skip value
2144 //
2145 if (StrLen (&OptionString[Index]) != 0) {
2146 MenuOption->Row++;
2147 }
2148
2149 FreePool (OutputString);
2150 }
2151
2152 MenuOption->Row = OriginalRow;
2153
2154 FreePool (OptionString);
2155 } else {
2156 if (NewLine) {
2157 if (MenuOption->GrayOut) {
2158 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
2159 } else if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
2160 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
2161 }
2162
2163 OriginalRow = MenuOption->Row;
2164 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
2165
2166 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
2167 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2168 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2169 }
2170 //
2171 // If there is more string to process print on the next row and increment the Skip value
2172 //
2173 if (StrLen (&MenuOption->Description[Index]) != 0) {
2174 MenuOption->Row++;
2175 }
2176
2177 FreePool (OutputString);
2178 }
2179
2180 MenuOption->Row = OriginalRow;
2181 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2182 }
2183 }
2184 }
2185
2186 //
2187 // This is only possible if we entered this page and the first menu option is
2188 // a "non-menu" item. In that case, force it UiDown
2189 //
2190 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2191 if (!IsSelectable (MenuOption)) {
2192 ASSERT (ScreenOperation == UiNoOperation);
2193 ScreenOperation = UiDown;
2194 ControlFlag = CfScreenOperation;
2195 break;
2196 }
2197
2198 //
2199 // This is the current selected statement
2200 //
2201 Statement = MenuOption->ThisTag;
2202 Selection->Statement = Statement;
2203 //
2204 // Record highlight for current menu
2205 //
2206 CurrentMenu->QuestionId = Statement->QuestionId;
2207
2208 //
2209 // Set reverse attribute
2210 //
2211 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
2212 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2213
2214 //
2215 // Assuming that we have a refresh linked-list created, lets annotate the
2216 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2217 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2218 //
2219 if (gMenuRefreshHead != NULL) {
2220 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
2221 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
2222 if (MenuRefreshEntry->MenuOption == MenuOption) {
2223 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
2224 }
2225 }
2226 }
2227
2228 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2229 if (OptionString != NULL) {
2230 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
2231 //
2232 // If leading spaces on OptionString - remove the spaces
2233 //
2234 for (Index = 0; OptionString[Index] == L' '; Index++)
2235 ;
2236
2237 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
2238 OptionString[Count] = OptionString[Index];
2239 Count++;
2240 }
2241
2242 OptionString[Count] = CHAR_NULL;
2243 }
2244 Width = (UINT16) gOptionBlockWidth;
2245
2246 OriginalRow = MenuOption->Row;
2247
2248 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
2249 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2250 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2251 }
2252 //
2253 // If there is more string to process print on the next row and increment the Skip value
2254 //
2255 if (StrLen (&OptionString[Index]) != 0) {
2256 MenuOption->Row++;
2257 }
2258
2259 FreePool (OutputString);
2260 }
2261
2262 MenuOption->Row = OriginalRow;
2263
2264 FreePool (OptionString);
2265 } else {
2266 if (NewLine) {
2267 OriginalRow = MenuOption->Row;
2268
2269 Width = GetWidth (Statement, MenuOption->Handle);
2270
2271 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
2272 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2273 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2274 }
2275 //
2276 // If there is more string to process print on the next row and increment the Skip value
2277 //
2278 if (StrLen (&MenuOption->Description[Index]) != 0) {
2279 MenuOption->Row++;
2280 }
2281
2282 FreePool (OutputString);
2283 }
2284
2285 MenuOption->Row = OriginalRow;
2286
2287 }
2288 }
2289
2290 UpdateKeyHelp (Selection, MenuOption, FALSE);
2291
2292 //
2293 // Clear reverse attribute
2294 //
2295 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2296 }
2297 //
2298 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2299 // if we didn't break halfway when process CfRefreshHighLight.
2300 //
2301 Repaint = SavedValue;
2302 break;
2303
2304 case CfUpdateHelpString:
2305 ControlFlag = CfPrepareToReadKey;
2306
2307 if (Repaint || NewLine) {
2308 //
2309 // Don't print anything if it is a NULL help token
2310 //
2311 ASSERT(MenuOption != NULL);
2312 if (MenuOption->ThisTag->Help == 0) {
2313 StringPtr = L"\0";
2314 } else {
2315 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2316 }
2317
2318 ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
2319
2320 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
2321
2322 for (Index = 0; Index < BottomRow - TopRow; Index++) {
2323 //
2324 // Pad String with spaces to simulate a clearing of the previous line
2325 //
2326 for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth * 2]) / 2 < gHelpBlockWidth;) {
2327 StrCat (&FormattedString[Index * gHelpBlockWidth * 2], L" ");
2328 }
2329
2330 PrintStringAt (
2331 LocalScreen.RightColumn - gHelpBlockWidth,
2332 Index + TopRow,
2333 &FormattedString[Index * gHelpBlockWidth * 2]
2334 );
2335 }
2336 }
2337 //
2338 // Reset this flag every time we finish using it.
2339 //
2340 Repaint = FALSE;
2341 NewLine = FALSE;
2342 break;
2343
2344 case CfPrepareToReadKey:
2345 ControlFlag = CfReadKey;
2346 ScreenOperation = UiNoOperation;
2347 break;
2348
2349 case CfReadKey:
2350 ControlFlag = CfScreenOperation;
2351
2352 //
2353 // Wait for user's selection
2354 //
2355 do {
2356 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
2357 } while (Status == EFI_TIMEOUT);
2358
2359 if (Selection->Action == UI_ACTION_REFRESH_FORMSET) {
2360 //
2361 // IFR is updated in Callback of refresh opcode, re-parse it
2362 //
2363 Selection->Statement = NULL;
2364 return EFI_SUCCESS;
2365 }
2366
2367 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2368 //
2369 // If we encounter error, continue to read another key in.
2370 //
2371 if (EFI_ERROR (Status)) {
2372 ControlFlag = CfReadKey;
2373 break;
2374 }
2375
2376 switch (Key.UnicodeChar) {
2377 case CHAR_CARRIAGE_RETURN:
2378 ScreenOperation = UiSelect;
2379 gDirection = 0;
2380 break;
2381
2382 //
2383 // We will push the adjustment of these numeric values directly to the input handler
2384 // NOTE: we won't handle manual input numeric
2385 //
2386 case '+':
2387 case '-':
2388 //
2389 // If the screen has no menu items, and the user didn't select UiReset
2390 // ignore the selection and go back to reading keys.
2391 //
2392 if(IsListEmpty (&gMenuOption)) {
2393 ControlFlag = CfReadKey;
2394 break;
2395 }
2396
2397 ASSERT(MenuOption != NULL);
2398 Statement = MenuOption->ThisTag;
2399 if ((Statement->Operand == EFI_IFR_DATE_OP)
2400 || (Statement->Operand == EFI_IFR_TIME_OP)
2401 || ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
2402 ){
2403 if (Key.UnicodeChar == '+') {
2404 gDirection = SCAN_RIGHT;
2405 } else {
2406 gDirection = SCAN_LEFT;
2407 }
2408 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2409 if (EFI_ERROR (Status)) {
2410 //
2411 // Repaint to clear possible error prompt pop-up
2412 //
2413 Repaint = TRUE;
2414 NewLine = TRUE;
2415 } else {
2416 Selection->Action = UI_ACTION_REFRESH_FORM;
2417 }
2418 if (OptionString != NULL) {
2419 FreePool (OptionString);
2420 }
2421 }
2422 break;
2423
2424 case '^':
2425 ScreenOperation = UiUp;
2426 break;
2427
2428 case 'V':
2429 case 'v':
2430 ScreenOperation = UiDown;
2431 break;
2432
2433 case ' ':
2434 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
2435 //
2436 // If the screen has no menu items, and the user didn't select UiReset
2437 // ignore the selection and go back to reading keys.
2438 //
2439 if(IsListEmpty (&gMenuOption)) {
2440 ControlFlag = CfReadKey;
2441 break;
2442 }
2443
2444 ASSERT(MenuOption != NULL);
2445 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) {
2446 ScreenOperation = UiSelect;
2447 }
2448 }
2449 break;
2450
2451 case CHAR_NULL:
2452 if (((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2453 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2454 ) {
2455 //
2456 // If the function key has been disabled, just ignore the key.
2457 //
2458 } else {
2459 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2460 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2461 if (Key.ScanCode == SCAN_F9) {
2462 //
2463 // Reset to standard default
2464 //
2465 DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD;
2466 }
2467 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2468 break;
2469 }
2470 }
2471 }
2472 break;
2473 }
2474 break;
2475
2476 case CfScreenOperation:
2477 if (ScreenOperation != UiReset) {
2478 //
2479 // If the screen has no menu items, and the user didn't select UiReset
2480 // ignore the selection and go back to reading keys.
2481 //
2482 if (IsListEmpty (&gMenuOption)) {
2483 ControlFlag = CfReadKey;
2484 break;
2485 }
2486 //
2487 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2488 //
2489 for (Link = gMenuOption.ForwardLink; Link != &gMenuOption; Link = Link->ForwardLink) {
2490 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2491 if (IsSelectable (NextMenuOption)) {
2492 break;
2493 }
2494 }
2495
2496 if (Link == &gMenuOption) {
2497 ControlFlag = CfPrepareToReadKey;
2498 break;
2499 }
2500 }
2501
2502 for (Index = 0;
2503 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2504 Index++
2505 ) {
2506 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2507 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2508 break;
2509 }
2510 }
2511 break;
2512
2513 case CfUiSelect:
2514 ControlFlag = CfCheckSelection;
2515
2516 ASSERT(MenuOption != NULL);
2517 Statement = MenuOption->ThisTag;
2518 if ((Statement->Operand == EFI_IFR_TEXT_OP) ||
2519 (Statement->Operand == EFI_IFR_DATE_OP) ||
2520 (Statement->Operand == EFI_IFR_TIME_OP) ||
2521 (Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0)) {
2522 break;
2523 }
2524
2525 //
2526 // Keep highlight on current MenuOption
2527 //
2528 Selection->QuestionId = Statement->QuestionId;
2529
2530 switch (Statement->Operand) {
2531 case EFI_IFR_REF_OP:
2532 if (Statement->RefDevicePath != 0) {
2533 //
2534 // Goto another Hii Package list
2535 //
2536 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2537
2538 StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle);
2539 if (StringPtr == NULL) {
2540 //
2541 // No device path string not found, exit
2542 //
2543 Selection->Action = UI_ACTION_EXIT;
2544 Selection->Statement = NULL;
2545 break;
2546 }
2547 BufferSize = StrLen (StringPtr) / 2;
2548 DevicePath = AllocatePool (BufferSize);
2549 ASSERT (DevicePath != NULL);
2550
2551 //
2552 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2553 //
2554 DevicePathBuffer = (UINT8 *) DevicePath;
2555 for (Index = 0; StringPtr[Index] != L'\0'; Index ++) {
2556 TemStr[0] = StringPtr[Index];
2557 DigitUint8 = (UINT8) StrHexToUint64 (TemStr);
2558 if (DigitUint8 == 0 && TemStr[0] != L'0') {
2559 //
2560 // Invalid Hex Char as the tail.
2561 //
2562 break;
2563 }
2564 if ((Index & 1) == 0) {
2565 DevicePathBuffer [Index/2] = DigitUint8;
2566 } else {
2567 DevicePathBuffer [Index/2] = (UINT8) ((DevicePathBuffer [Index/2] << 4) + DigitUint8);
2568 }
2569 }
2570
2571 Selection->Handle = DevicePathToHiiHandle (DevicePath);
2572 if (Selection->Handle == NULL) {
2573 //
2574 // If target Hii Handle not found, exit
2575 //
2576 Selection->Action = UI_ACTION_EXIT;
2577 Selection->Statement = NULL;
2578 break;
2579 }
2580
2581 FreePool (StringPtr);
2582 FreePool (DevicePath);
2583
2584 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2585 Selection->FormId = Statement->RefFormId;
2586 Selection->QuestionId = Statement->RefQuestionId;
2587 } else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) {
2588 //
2589 // Goto another Formset, check for uncommitted data
2590 //
2591 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2592
2593 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2594 Selection->FormId = Statement->RefFormId;
2595 Selection->QuestionId = Statement->RefQuestionId;
2596 } else if (Statement->RefFormId != 0) {
2597 //
2598 // Check whether target From is suppressed.
2599 //
2600 RefForm = IdToForm (Selection->FormSet, Statement->RefFormId);
2601
2602 if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) {
2603 Status = EvaluateExpression (Selection->FormSet, RefForm, RefForm->SuppressExpression);
2604 if (EFI_ERROR (Status)) {
2605 return Status;
2606 }
2607
2608 if (RefForm->SuppressExpression->Result.Value.b) {
2609 //
2610 // Form is suppressed.
2611 //
2612 do {
2613 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString);
2614 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2615
2616 Repaint = TRUE;
2617 break;
2618 }
2619 }
2620
2621 //
2622 // Goto another form inside this formset,
2623 //
2624 Selection->Action = UI_ACTION_REFRESH_FORM;
2625
2626 //
2627 // Link current form so that we can always go back when someone hits the ESC
2628 //
2629 MenuList = UiFindMenuList (&Selection->FormSetGuid, Statement->RefFormId);
2630 if (MenuList == NULL) {
2631 MenuList = UiAddMenuList (CurrentMenu, &Selection->FormSetGuid, Statement->RefFormId);
2632 }
2633
2634 Selection->FormId = Statement->RefFormId;
2635 Selection->QuestionId = Statement->RefQuestionId;
2636 } else if (Statement->RefQuestionId != 0) {
2637 //
2638 // Goto another Question
2639 //
2640 Selection->QuestionId = Statement->RefQuestionId;
2641
2642 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2643 Selection->Action = UI_ACTION_REFRESH_FORM;
2644 } else {
2645 Repaint = TRUE;
2646 NewLine = TRUE;
2647 break;
2648 }
2649 }
2650 break;
2651
2652 case EFI_IFR_ACTION_OP:
2653 //
2654 // Process the Config string <ConfigResp>
2655 //
2656 Status = ProcessQuestionConfig (Selection, Statement);
2657
2658 if (EFI_ERROR (Status)) {
2659 break;
2660 }
2661
2662 //
2663 // The action button may change some Question value, so refresh the form
2664 //
2665 Selection->Action = UI_ACTION_REFRESH_FORM;
2666 break;
2667
2668 case EFI_IFR_RESET_BUTTON_OP:
2669 //
2670 // Reset Question to default value specified by DefaultId
2671 //
2672 ControlFlag = CfUiDefault;
2673 DefaultId = Statement->DefaultId;
2674 break;
2675
2676 default:
2677 //
2678 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2679 //
2680 UpdateKeyHelp (Selection, MenuOption, TRUE);
2681 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2682
2683 if (EFI_ERROR (Status)) {
2684 Repaint = TRUE;
2685 NewLine = TRUE;
2686 UpdateKeyHelp (Selection, MenuOption, FALSE);
2687 } else {
2688 Selection->Action = UI_ACTION_REFRESH_FORM;
2689 }
2690
2691 if (OptionString != NULL) {
2692 FreePool (OptionString);
2693 }
2694 break;
2695 }
2696 break;
2697
2698 case CfUiReset:
2699 //
2700 // We come here when someone press ESC
2701 //
2702 ControlFlag = CfCheckSelection;
2703
2704 if (CurrentMenu->Parent != NULL) {
2705 //
2706 // we have a parent, so go to the parent menu
2707 //
2708 if (CompareGuid (&CurrentMenu->FormSetGuid, &CurrentMenu->Parent->FormSetGuid)) {
2709 //
2710 // The parent menu and current menu are in the same formset
2711 //
2712 Selection->Action = UI_ACTION_REFRESH_FORM;
2713 } else {
2714 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2715 }
2716 Selection->Statement = NULL;
2717
2718 Selection->FormId = CurrentMenu->Parent->FormId;
2719 Selection->QuestionId = CurrentMenu->Parent->QuestionId;
2720
2721 //
2722 // Clear highlight record for this menu
2723 //
2724 CurrentMenu->QuestionId = 0;
2725 break;
2726 }
2727
2728 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE) {
2729 //
2730 // We never exit FrontPage, so skip the ESC
2731 //
2732 Selection->Action = UI_ACTION_NONE;
2733 break;
2734 }
2735
2736 //
2737 // We are going to leave current FormSet, so check uncommited data in this FormSet
2738 //
2739 if (gNvUpdateRequired) {
2740 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2741
2742 YesResponse = gYesResponse[0];
2743 NoResponse = gNoResponse[0];
2744
2745 //
2746 // If NV flag is up, prompt user
2747 //
2748 do {
2749 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveChanges, gAreYouSure, gEmptyString);
2750 } while
2751 (
2752 (Key.ScanCode != SCAN_ESC) &&
2753 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
2754 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
2755 );
2756
2757 if (Key.ScanCode == SCAN_ESC) {
2758 //
2759 // User hits the ESC key
2760 //
2761 Repaint = TRUE;
2762 NewLine = TRUE;
2763
2764 Selection->Action = UI_ACTION_NONE;
2765 break;
2766 }
2767
2768 //
2769 // If the user hits the YesResponse key
2770 //
2771 if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
2772 Status = SubmitForm (Selection->FormSet, Selection->Form);
2773 }
2774 }
2775
2776 Selection->Action = UI_ACTION_EXIT;
2777 Selection->Statement = NULL;
2778 CurrentMenu->QuestionId = 0;
2779
2780 return EFI_SUCCESS;
2781
2782 case CfUiLeft:
2783 ControlFlag = CfCheckSelection;
2784 ASSERT(MenuOption != NULL);
2785 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2786 if (MenuOption->Sequence != 0) {
2787 //
2788 // In the middle or tail of the Date/Time op-code set, go left.
2789 //
2790 ASSERT(NewPos != NULL);
2791 NewPos = NewPos->BackLink;
2792 }
2793 }
2794 break;
2795
2796 case CfUiRight:
2797 ControlFlag = CfCheckSelection;
2798 ASSERT(MenuOption != NULL);
2799 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2800 if (MenuOption->Sequence != 2) {
2801 //
2802 // In the middle or tail of the Date/Time op-code set, go left.
2803 //
2804 ASSERT(NewPos != NULL);
2805 NewPos = NewPos->ForwardLink;
2806 }
2807 }
2808 break;
2809
2810 case CfUiUp:
2811 ControlFlag = CfCheckSelection;
2812
2813 SavedListEntry = TopOfScreen;
2814
2815 ASSERT(NewPos != NULL);
2816 if (NewPos->BackLink != &gMenuOption) {
2817 NewLine = TRUE;
2818 //
2819 // Adjust Date/Time position before we advance forward.
2820 //
2821 AdjustDateAndTimePosition (TRUE, &NewPos);
2822
2823 //
2824 // Caution that we have already rewind to the top, don't go backward in this situation.
2825 //
2826 if (NewPos->BackLink != &gMenuOption) {
2827 NewPos = NewPos->BackLink;
2828 }
2829
2830 Difference = MoveToNextStatement (TRUE, &NewPos);
2831 PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2832 DistanceValue = PreviousMenuOption->Skip;
2833
2834 //
2835 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2836 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2837 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2838 // checking can be done.
2839 //
2840 DistanceValue += AdjustDateAndTimePosition (TRUE, &NewPos);
2841
2842 ASSERT (MenuOption != NULL);
2843 if (Difference < 0) {
2844 //
2845 // We want to goto previous MenuOption, but finally we go down.
2846 // it means that we hit the begining MenuOption that can be focused
2847 // so we simply scroll to the top
2848 //
2849 if (SavedListEntry != gMenuOption.ForwardLink) {
2850 TopOfScreen = gMenuOption.ForwardLink;
2851 Repaint = TRUE;
2852 }
2853 } else if ((INTN) MenuOption->Row - (INTN) DistanceValue - Difference < (INTN) TopRow) {
2854 //
2855 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2856 //
2857 TopOfScreen = NewPos;
2858 Repaint = TRUE;
2859 SkipValue = 0;
2860 OldSkipValue = 0;
2861 }
2862
2863 //
2864 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2865 //
2866 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2867
2868 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2869 } else {
2870 SavedMenuOption = MenuOption;
2871 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2872 if (!IsSelectable (MenuOption)) {
2873 //
2874 // If we are at the end of the list and sitting on a text op, we need to more forward
2875 //
2876 ScreenOperation = UiDown;
2877 ControlFlag = CfScreenOperation;
2878 break;
2879 }
2880
2881 MenuOption = SavedMenuOption;
2882 }
2883 break;
2884
2885 case CfUiPageUp:
2886 ControlFlag = CfCheckSelection;
2887
2888 ASSERT(NewPos != NULL);
2889 if (NewPos->BackLink == &gMenuOption) {
2890 NewLine = FALSE;
2891 Repaint = FALSE;
2892 break;
2893 }
2894
2895 NewLine = TRUE;
2896 Repaint = TRUE;
2897 Link = TopOfScreen;
2898 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2899 Index = BottomRow;
2900 while ((Index >= TopRow) && (Link->BackLink != &gMenuOption)) {
2901 Index = Index - PreviousMenuOption->Skip;
2902 Link = Link->BackLink;
2903 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2904 }
2905
2906 TopOfScreen = Link;
2907 Difference = MoveToNextStatement (TRUE, &Link);
2908 if (Difference > 0) {
2909 //
2910 // The focus MenuOption is above the TopOfScreen
2911 //
2912 TopOfScreen = Link;
2913 } else if (Difference < 0) {
2914 //
2915 // This happens when there is no MenuOption can be focused from
2916 // Current MenuOption to the first MenuOption
2917 //
2918 TopOfScreen = gMenuOption.ForwardLink;
2919 }
2920 Index += Difference;
2921 if (Index < TopRow) {
2922 MenuOption = NULL;
2923 }
2924
2925 if (NewPos == Link) {
2926 Repaint = FALSE;
2927 NewLine = FALSE;
2928 } else {
2929 NewPos = Link;
2930 }
2931
2932 //
2933 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2934 // Don't do this when we are already in the first page.
2935 //
2936 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2937 AdjustDateAndTimePosition (TRUE, &NewPos);
2938 break;
2939
2940 case CfUiPageDown:
2941 ControlFlag = CfCheckSelection;
2942
2943 ASSERT (NewPos != NULL);
2944 if (NewPos->ForwardLink == &gMenuOption) {
2945 NewLine = FALSE;
2946 Repaint = FALSE;
2947 break;
2948 }
2949
2950 NewLine = TRUE;
2951 Repaint = TRUE;
2952 Link = TopOfScreen;
2953 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2954 Index = TopRow;
2955 while ((Index <= BottomRow) && (Link->ForwardLink != &gMenuOption)) {
2956 Index = Index + NextMenuOption->Skip;
2957 Link = Link->ForwardLink;
2958 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2959 }
2960
2961 Index += MoveToNextStatement (FALSE, &Link);
2962 if (Index > BottomRow) {
2963 //
2964 // There are more MenuOption needing scrolling
2965 //
2966 TopOfScreen = Link;
2967 MenuOption = NULL;
2968 }
2969 if (NewPos == Link && Index <= BottomRow) {
2970 //
2971 // Finally we know that NewPos is the last MenuOption can be focused.
2972 //
2973 NewLine = FALSE;
2974 Repaint = FALSE;
2975 } else {
2976 NewPos = Link;
2977 }
2978
2979 //
2980 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2981 // Don't do this when we are already in the last page.
2982 //
2983 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2984 AdjustDateAndTimePosition (TRUE, &NewPos);
2985 break;
2986
2987 case CfUiDown:
2988 ControlFlag = CfCheckSelection;
2989 //
2990 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2991 // to be one that progresses to the next set of op-codes, we need to advance to the last
2992 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2993 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2994 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2995 // the Date/Time op-code.
2996 //
2997 SavedListEntry = NewPos;
2998 DistanceValue = AdjustDateAndTimePosition (FALSE, &NewPos);
2999
3000 if (NewPos->ForwardLink != &gMenuOption) {
3001 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3002 NewLine = TRUE;
3003 NewPos = NewPos->ForwardLink;
3004
3005 DistanceValue += MoveToNextStatement (FALSE, &NewPos);
3006 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3007
3008 //
3009 // An option might be multi-line, so we need to reflect that data in the overall skip value
3010 //
3011 UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, (UINTN) SkipValue);
3012 DistanceValue += NextMenuOption->Skip;
3013
3014 Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
3015 if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
3016 (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
3017 NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
3018 ) {
3019 Temp ++;
3020 }
3021
3022 //
3023 // If we are going to scroll, update TopOfScreen
3024 //
3025 if (Temp > BottomRow) {
3026 do {
3027 //
3028 // Is the current top of screen a zero-advance op-code?
3029 // If so, keep moving forward till we hit a >0 advance op-code
3030 //
3031 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3032
3033 //
3034 // If bottom op-code is more than one line or top op-code is more than one line
3035 //
3036 if ((DistanceValue > 1) || (MenuOption->Skip > 1)) {
3037 //
3038 // Is the bottom op-code greater than or equal in size to the top op-code?
3039 //
3040 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
3041 //
3042 // Skip the top op-code
3043 //
3044 TopOfScreen = TopOfScreen->ForwardLink;
3045 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
3046
3047 OldSkipValue = Difference;
3048
3049 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3050
3051 //
3052 // If we have a remainder, skip that many more op-codes until we drain the remainder
3053 //
3054 while (Difference >= (INTN) SavedMenuOption->Skip) {
3055 //
3056 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3057 //
3058 Difference = Difference - (INTN) SavedMenuOption->Skip;
3059 TopOfScreen = TopOfScreen->ForwardLink;
3060 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3061 }
3062 //
3063 // Since we will act on this op-code in the next routine, and increment the
3064 // SkipValue, set the skips to one less than what is required.
3065 //
3066 SkipValue = Difference - 1;
3067
3068 } else {
3069 //
3070 // Since we will act on this op-code in the next routine, and increment the
3071 // SkipValue, set the skips to one less than what is required.
3072 //
3073 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
3074 }
3075 } else {
3076 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
3077 TopOfScreen = TopOfScreen->ForwardLink;
3078 break;
3079 } else {
3080 SkipValue = OldSkipValue;
3081 }
3082 }
3083 //
3084 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3085 // Let's set a skip flag to smoothly scroll the top of the screen.
3086 //
3087 if (SavedMenuOption->Skip > 1) {
3088 if (SavedMenuOption == NextMenuOption) {
3089 SkipValue = 0;
3090 } else {
3091 SkipValue++;
3092 }
3093 } else if (SavedMenuOption->Skip == 1) {
3094 SkipValue = 0;
3095 } else {
3096 SkipValue = 0;
3097 TopOfScreen = TopOfScreen->ForwardLink;
3098 }
3099 } while (SavedMenuOption->Skip == 0);
3100
3101 Repaint = TRUE;
3102 OldSkipValue = SkipValue;
3103 }
3104
3105 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3106
3107 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3108
3109 } else {
3110 SavedMenuOption = MenuOption;
3111 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3112 if (!IsSelectable (MenuOption)) {
3113 //
3114 // If we are at the end of the list and sitting on a text op, we need to more forward
3115 //
3116 ScreenOperation = UiUp;
3117 ControlFlag = CfScreenOperation;
3118 break;
3119 }
3120
3121 MenuOption = SavedMenuOption;
3122 //
3123 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3124 //
3125 AdjustDateAndTimePosition (TRUE, &NewPos);
3126 }
3127 break;
3128
3129 case CfUiSave:
3130 ControlFlag = CfCheckSelection;
3131
3132 //
3133 // Submit the form
3134 //
3135 Status = SubmitForm (Selection->FormSet, Selection->Form);
3136
3137 if (!EFI_ERROR (Status)) {
3138 ASSERT(MenuOption != NULL);
3139 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3140 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
3141 } else {
3142 do {
3143 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString);
3144 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3145
3146 Repaint = TRUE;
3147 NewLine = TRUE;
3148 }
3149 break;
3150
3151 case CfUiDefault:
3152 ControlFlag = CfCheckSelection;
3153 if (!Selection->FormEditable) {
3154 //
3155 // This Form is not editable, ignore the F9 (reset to default)
3156 //
3157 break;
3158 }
3159
3160 Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId);
3161
3162 if (!EFI_ERROR (Status)) {
3163 Selection->Action = UI_ACTION_REFRESH_FORM;
3164 Selection->Statement = NULL;
3165
3166 //
3167 // Show NV update flag on status bar
3168 //
3169 gNvUpdateRequired = TRUE;
3170 }
3171 break;
3172
3173 case CfUiNoOperation:
3174 ControlFlag = CfCheckSelection;
3175 break;
3176
3177 case CfExit:
3178 UiFreeRefreshList ();
3179
3180 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
3181 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
3182 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
3183 gST->ConOut->OutputString (gST->ConOut, L"\n");
3184
3185 return EFI_SUCCESS;
3186
3187 default:
3188 break;
3189 }
3190 }
3191 }