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