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