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