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