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