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