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