1 /** @file
2 Implementation for Hii Popup Protocol.
3 
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "FormDisplay.h"
10 
11 EFI_SCREEN_DESCRIPTOR  gPopupDimensions;
12 LIST_ENTRY             gUserSelectableOptions;
13 EFI_STRING             gMessageString;
14 UINTN                  gMesStrLineNum;
15 UINTN                  gMaxRowWidth;
16 
17 /**
18   Free the user selectable option structure data.
19 
20   @param  OptionList  Point to the selectable option list which need to be freed.
21 
22 **/
23 VOID
FreeSelectableOptions(LIST_ENTRY * OptionList)24 FreeSelectableOptions(
25   LIST_ENTRY           *OptionList
26   )
27 {
28   LIST_ENTRY              *Link;
29   USER_SELECTABLE_OPTION  *SelectableOption;
30 
31   while (!IsListEmpty (OptionList)) {
32     Link = GetFirstNode (OptionList);
33     SelectableOption = SELECTABLE_OPTION_FROM_LINK (Link);
34     RemoveEntryList (&SelectableOption->Link);
35     FreePool (SelectableOption);
36   }
37 }
38 
39 /**
40   Display one selectable option.
41 
42   @param  SelectableOption  The selectable option need to be drew.
43   @param  Highlight         Whether the option need to be highlighted.
44 
45 **/
46 VOID
DisplayOneSelectableOption(IN USER_SELECTABLE_OPTION * SelectableOption,IN BOOLEAN Highlight)47 DisplayOneSelectableOption(
48   IN USER_SELECTABLE_OPTION    *SelectableOption,
49   IN BOOLEAN                   Highlight
50   )
51 {
52   if (Highlight) {
53     gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
54   }
55   PrintStringAt (SelectableOption->OptionCol, SelectableOption->OptionRow, SelectableOption->OptionString);
56   gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
57 }
58 
59 /**
60   Add one selectable option to option list. This is the work function for AddUserSelectableOptions.
61 
62   @param  PopupType     The option need to be drew.
63   @param  OptionType    The type of this selection option.
64   @param  OptionString  Point to the option string that to be shown.
65   @param  OptionCol     The column that the option need to be drew at.
66   @param  OptionRow     The row that the option need to be drew at.
67 
68   @retval  EFI_SUCCESS           This function implement successfully.
69   @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available.
70 
71 **/
72 EFI_STATUS
AddOneSelectableOption(IN EFI_HII_POPUP_TYPE PopupType,IN EFI_HII_POPUP_SELECTION OptionType,IN CHAR16 * OptionString,IN UINTN OptionCol,IN UINTN OptionRow)73 AddOneSelectableOption (
74   IN EFI_HII_POPUP_TYPE           PopupType,
75   IN EFI_HII_POPUP_SELECTION      OptionType,
76   IN CHAR16                       *OptionString,
77   IN UINTN                        OptionCol,
78   IN UINTN                        OptionRow
79   )
80 {
81   USER_SELECTABLE_OPTION  *UserSelectableOption;
82 
83   UserSelectableOption = AllocateZeroPool (sizeof (USER_SELECTABLE_OPTION));
84   if (UserSelectableOption == NULL) {
85     return EFI_OUT_OF_RESOURCES;
86   }
87   //
88   // Initialize the user selectable option based on the PopupType and OptionType.
89   // And then add the option to the option list gUserSelectableOptions.
90   //
91   UserSelectableOption->Signature = USER_SELECTABLE_OPTION_SIGNATURE;
92   UserSelectableOption->OptionString = OptionString;
93   UserSelectableOption->OptionType = OptionType;
94   UserSelectableOption->OptionCol = OptionCol;
95   UserSelectableOption->OptionRow = OptionRow;
96   UserSelectableOption->MinSequence = 0;
97 
98   switch (PopupType) {
99   case EfiHiiPopupTypeOk:
100     UserSelectableOption->MaxSequence = 0;
101     UserSelectableOption->Sequence= 0;
102     break;
103   case EfiHiiPopupTypeOkCancel:
104     UserSelectableOption->MaxSequence = 1;
105     if (OptionType == EfiHiiPopupSelectionOk) {
106       UserSelectableOption->Sequence= 0;
107     } else {
108       UserSelectableOption->Sequence= 1;
109     }
110     break;
111   case EfiHiiPopupTypeYesNo:
112     UserSelectableOption->MaxSequence = 1;
113     if (OptionType == EfiHiiPopupSelectionYes) {
114       UserSelectableOption->Sequence = 0;
115     } else {
116       UserSelectableOption->Sequence = 1;
117     }
118     break;
119   case EfiHiiPopupTypeYesNoCancel:
120     UserSelectableOption->MaxSequence = 2;
121     if (OptionType == EfiHiiPopupSelectionYes) {
122       UserSelectableOption->Sequence = 0;
123     } else if (OptionType == EfiHiiPopupSelectionNo){
124       UserSelectableOption->Sequence = 1;
125     } else {
126       UserSelectableOption->Sequence = 2;
127     }
128     break;
129   default:
130     break;
131   }
132   InsertTailList (&gUserSelectableOptions, &UserSelectableOption->Link);
133 
134   return EFI_SUCCESS;
135 }
136 
137 /**
138   Add user selectable options to option list for different types of Popup.
139 
140   @param  PopupType    Type of the popup to display.
141 
142   @retval  EFI_SUCCESS           This function implement successfully.
143   @retval  EFI_OUT_OF_RESOURCES  There are not enough resources available.
144 
145 **/
146 EFI_STATUS
AddUserSelectableOptions(IN EFI_HII_POPUP_TYPE PopupType)147 AddUserSelectableOptions (
148   IN  EFI_HII_POPUP_TYPE  PopupType
149   )
150 {
151   EFI_STATUS   Status;
152   UINTN        EndCol;
153   UINTN        StartCol;
154   UINTN        OptionCol;
155   UINTN        OptionRow;
156   UINTN        ColDimension;
157 
158   Status = EFI_SUCCESS;
159   EndCol = gPopupDimensions.RightColumn;
160   StartCol = gPopupDimensions.LeftColumn;
161   OptionRow = gPopupDimensions.BottomRow - POPUP_BORDER;
162   ColDimension = EndCol - StartCol + 1;
163 
164   InitializeListHead (&gUserSelectableOptions);
165 
166   switch (PopupType) {
167   case EfiHiiPopupTypeOk:
168     //
169     // Add [Ok] option to the option list.
170     //
171     OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_WIDTH) / 2;
172     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow);
173     break;
174   case EfiHiiPopupTypeOkCancel:
175     //
176     // Add [Ok] and [Cancel] options to the option list.
177     //
178     OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3;
179     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionOk, gOkOption, OptionCol, OptionRow);
180     OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_OK_CAL_WIDTH) / 3 - (GetStringWidth (gCancelOption) -2) / 2 + 1;
181     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow);
182     break;
183   case EfiHiiPopupTypeYesNo:
184     //
185     // Add [Yes] and [No] options to the option list.
186     //
187     OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3;
188     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow);
189     OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_WIDTH) / 3 - (GetStringWidth (gNoOption)- 2) / 2 + 1;
190     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow);
191     break;
192   case EfiHiiPopupTypeYesNoCancel:
193     //
194     // Add [Yes], [No] and [Cancel] options to the option list.
195     //
196     OptionCol = StartCol + (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4;
197     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionYes, gYesOption, OptionCol, OptionRow);
198     OptionCol = StartCol + (ColDimension - (GetStringWidth (gNoOption) -2) / 2) / 2;
199     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionNo, gNoOption, OptionCol, OptionRow);
200     OptionCol = EndCol - (ColDimension - USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH) / 4 - (GetStringWidth (gCancelOption) - 2) / 2 + 1;
201     Status = AddOneSelectableOption (PopupType, EfiHiiPopupSelectionCancel, gCancelOption, OptionCol, OptionRow);
202     break;
203   default:
204     break;
205   }
206   return Status;
207 }
208 
209 /**
210   Show selectable options to user and get the one that user select.
211 
212   @param  PopupType      Type of the popup to display.
213   @param  UserSelection  User selection.
214 
215 **/
216 VOID
GetUserSelection(IN EFI_HII_POPUP_TYPE PopupType,OUT EFI_HII_POPUP_SELECTION * UserSelection)217 GetUserSelection (
218   IN  EFI_HII_POPUP_TYPE       PopupType,
219   OUT EFI_HII_POPUP_SELECTION  *UserSelection
220   )
221 {
222   LIST_ENTRY                       *HighlightPos;
223   LIST_ENTRY                       *Link;
224   USER_SELECTABLE_OPTION           *SelectableOption;
225   USER_SELECTABLE_OPTION           *HighlightOption;
226   EFI_INPUT_KEY                    KeyValue;
227   EFI_STATUS                       Status;
228 
229   //
230   // Display user selectable options in gUserSelectableOptions and get the option which user selects.
231   //
232   HighlightPos = gUserSelectableOptions.ForwardLink;
233   do {
234     for (Link = gUserSelectableOptions.ForwardLink; Link != &gUserSelectableOptions; Link = Link->ForwardLink) {
235       SelectableOption          = SELECTABLE_OPTION_FROM_LINK (Link);
236       DisplayOneSelectableOption (SelectableOption, (BOOLEAN)(Link == HighlightPos));
237     }
238     //
239     //If UserSelection is NULL, there is no need to handle the key user input, just return.
240     //
241     if (UserSelection == NULL) {
242       return;
243     }
244 
245     Status = WaitForKeyStroke (&KeyValue);
246     ASSERT_EFI_ERROR (Status);
247 
248     HighlightOption = SELECTABLE_OPTION_FROM_LINK (HighlightPos);
249     switch (KeyValue.UnicodeChar) {
250     case CHAR_NULL:
251       switch (KeyValue.ScanCode) {
252       case SCAN_RIGHT:
253         if (HighlightOption->Sequence < HighlightOption->MaxSequence) {
254           HighlightPos = HighlightPos->ForwardLink;
255         } else {
256           HighlightPos = gUserSelectableOptions.ForwardLink;
257         }
258         break;
259       case SCAN_LEFT:
260         if (HighlightOption->Sequence > HighlightOption->MinSequence) {
261           HighlightPos = HighlightPos->BackLink;
262         } else {
263           HighlightPos = gUserSelectableOptions.BackLink;
264         }
265         break;
266       default:
267         break;
268       }
269       break;
270 
271     case CHAR_CARRIAGE_RETURN:
272       *UserSelection = HighlightOption->OptionType;
273       return;
274     default:
275       if (((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptYes | UPPER_LOWER_CASE_OFFSET)) &&
276         (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)) {
277         *UserSelection = EfiHiiPopupSelectionYes;
278         return;
279       } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptNo| UPPER_LOWER_CASE_OFFSET) &&
280         (PopupType == EfiHiiPopupTypeYesNo || PopupType == EfiHiiPopupTypeYesNoCancel)){
281         *UserSelection = EfiHiiPopupSelectionNo;
282         return;
283       } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptOk | UPPER_LOWER_CASE_OFFSET) &&
284         (PopupType == EfiHiiPopupTypeOk || PopupType == EfiHiiPopupTypeOkCancel)){
285         *UserSelection = EfiHiiPopupSelectionOk;
286         return;
287       } else if ((KeyValue.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (*gConfirmOptCancel| UPPER_LOWER_CASE_OFFSET) &&
288         (PopupType == EfiHiiPopupTypeOkCancel || PopupType == EfiHiiPopupTypeYesNoCancel)){
289         *UserSelection = EfiHiiPopupSelectionCancel;
290         return;
291       }
292       break;
293     }
294   } while (TRUE);
295 }
296 
297 /**
298   Get the offset in the input string when the width reaches to a fixed one.
299 
300   The input string may contain NARROW_CHAR and WIDE_CHAR.
301   Notice: the input string doesn't contain line break characters.
302 
303   @param  String      The input string to be counted.
304   @param  MaxWidth    The max length this function supported.
305   @param  Offset      The max index of the string can be show out. If string's width less than MaxWidth, offset will point to the "\0" of the string.
306 
307 **/
308 VOID
GetStringOffsetWithWidth(IN CHAR16 * String,IN UINTN MaxWidth,OUT UINTN * Offset)309 GetStringOffsetWithWidth (
310   IN  CHAR16               *String,
311   IN  UINTN                MaxWidth,
312   OUT UINTN                *Offset
313   )
314 {
315   UINTN   StringWidth;
316   UINTN   CharWidth;
317   UINTN   StrOffset;
318 
319   StringWidth = 0;
320   CharWidth   = 1;
321 
322   for (StrOffset = 0; String[StrOffset] != CHAR_NULL; StrOffset++) {
323     switch (String[StrOffset]) {
324     case NARROW_CHAR:
325       CharWidth = 1;
326       break;
327     case WIDE_CHAR:
328       CharWidth = 2;
329       break;
330     default:
331       StringWidth += CharWidth;
332       if (StringWidth >= MaxWidth) {
333         *Offset = StrOffset;
334         return;
335       }
336     }
337   }
338   *Offset = StrOffset;
339 }
340 
341 /**
342   Parse the message to check if it contains line break characters.
343   For once call, caller can get the string for one line and the width of the string.
344   This function call be called recursively to parse the whole InputString.
345 
346   (Notice: current implementation, it only checks \r, \n characters, it deals \r,\n,\n\r same as \r\n.)
347 
348   @param  InputString       String description for this option.
349   @param  OutputString      Buffer to copy the string into, caller is responsible for freeing the buffer.
350   @param  OutputStrWidth    The width of OutputString.
351   @param  Index             Where in InputString to start the copy process
352 
353   @return Returns the number of CHAR16 characters that were copied into the OutputString buffer, include the '\0' info.
354 
355 **/
356 UINTN
ParseMessageString(IN CHAR16 * InputString,OUT CHAR16 ** OutputString,OUT UINTN * OutputStrWidth,IN OUT UINTN * Index)357 ParseMessageString (
358   IN     CHAR16    *InputString,
359   OUT    CHAR16    **OutputString,
360   OUT    UINTN     *OutputStrWidth,
361   IN OUT UINTN     *Index
362   )
363 {
364   UINTN          StrOffset;
365 
366   if (InputString == NULL || Index == NULL || OutputString == NULL) {
367     return 0;
368   }
369 
370   *OutputStrWidth = 0;
371 
372   //
373   //Check the string to see if there are line break characters in the string
374   //
375   for (StrOffset = 0;
376     InputString[*Index + StrOffset] != CHAR_CARRIAGE_RETURN && InputString[*Index + StrOffset] != CHAR_LINEFEED && InputString[*Index + StrOffset] != CHAR_NULL;
377     StrOffset++
378   );
379 
380   //
381   // The CHAR_NULL has process last time, this time just return 0 to stand for finishing parsing the InputString.
382   //
383   if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
384     return 0;
385   }
386 
387   //
388   // Copy the string to OutputString buffer and calculate the width of OutputString.
389   //
390   *OutputString = AllocateZeroPool ((StrOffset + 1) * sizeof(CHAR16));
391   if (*OutputString == NULL) {
392     return 0;
393   }
394   CopyMem ((*OutputString), &InputString[*Index], StrOffset * sizeof(CHAR16));
395   *OutputStrWidth = (GetStringWidth (*OutputString) -2) / 2;
396 
397   //
398   // Update the value of Index, can be used for marking where to check the input string for next call.
399   //
400   if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
401     //
402     // Skip the /n or /n/r info.
403     //
404     if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
405       *Index = (*Index + StrOffset + 2);
406     } else {
407       *Index = (*Index + StrOffset + 1);
408     }
409   } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
410     //
411     // Skip the /r or /r/n info.
412     //
413     if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
414       *Index = (*Index + StrOffset + 2);
415     } else {
416       *Index = (*Index + StrOffset + 1);
417     }
418   } else {
419     *Index = (*Index + StrOffset);
420   }
421 
422   return StrOffset + 1;
423 }
424 
425 /**
426   Calculate the position of the popup.
427 
428   @param  PopupType       Type of the popup to display.
429   @param  ScreenForPopup  The screen dimensions for the popup.
430 
431 **/
432 VOID
CalculatePopupPosition(IN EFI_HII_POPUP_TYPE PopupType,OUT EFI_SCREEN_DESCRIPTOR * ScreenForPopup)433 CalculatePopupPosition (
434   IN  EFI_HII_POPUP_TYPE     PopupType,
435   OUT EFI_SCREEN_DESCRIPTOR  *ScreenForPopup
436   )
437 {
438   CHAR16              *OutputString;
439   UINTN               StringIndex;
440   UINTN               OutputStrWidth;
441   UINTN               OptionRowWidth;
442   UINTN               Columns;
443   UINTN               Rows;
444 
445   OptionRowWidth = 0;
446 
447   //
448   // Calculate the row number which is needed to show the message string and the max width of the string in one row.
449   //
450   for (StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth, &StringIndex) != 0;) {
451     gMesStrLineNum ++;
452     if (gMaxRowWidth < OutputStrWidth) {
453       gMaxRowWidth = OutputStrWidth;
454     }
455     FreePool (OutputString);
456   }
457 
458   //
459   // Calculate the row width for the selectable options.(OptionRowWidth = Number * SkipWidth + OptionWidth)
460   //
461   if (PopupType == EfiHiiPopupTypeOk) {
462     OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *2 + USER_SELECTABLE_OPTION_OK_WIDTH;
463   } else if (PopupType == EfiHiiPopupTypeOkCancel) {
464     OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_OK_CAL_WIDTH;
465   } else if (PopupType == EfiHiiPopupTypeYesNo) {
466     OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *3 + USER_SELECTABLE_OPTION_YES_NO_WIDTH;
467   } else if (PopupType == EfiHiiPopupTypeYesNoCancel) {
468     OptionRowWidth = USER_SELECTABLE_OPTION_SKIP_WIDTH *4 + USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH;
469   }
470   if (OptionRowWidth > gMaxRowWidth) {
471     gMaxRowWidth = OptionRowWidth;
472   }
473 
474   //
475   // Avialble row width for message string = screen width - left popup border width - right popup border width.
476   // Avialble line number for message string = screen height - 1 - popup header height -  popup footer height.
477   // (Notice: screen height - 1 because in current UI page, the bottom row of srceen is usded to show Status Bar,not for form itself.
478   // So we don't use the bottom row for popup either. If macro STATUS_BAR_HEIGHT changed, we also need to update the height here.)
479   //
480   // Select the smaller one between actual dimension of message string and the avialble dimension for message string.
481   //
482   gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Columns, &Rows);
483   gMaxRowWidth = MIN (gMaxRowWidth, Columns - 2 * POPUP_BORDER);
484   gMesStrLineNum = MIN (gMesStrLineNum, Rows -1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT);
485 
486   //
487   // Calculate the start column, end column, top row and bottom row for the popup.
488   //
489   ScreenForPopup->LeftColumn = (Columns -2 * POPUP_BORDER - gMaxRowWidth) / 2;
490   ScreenForPopup->RightColumn = ScreenForPopup->LeftColumn + gMaxRowWidth + 2 * POPUP_BORDER - 1;
491   ScreenForPopup->TopRow = (Rows - 1 - POPUP_FOOTER_HEIGHT - POPUP_HEADER_HEIGHT - gMesStrLineNum) / 2;
492   ScreenForPopup->BottomRow = ScreenForPopup->TopRow + gMesStrLineNum + POPUP_FOOTER_HEIGHT + POPUP_HEADER_HEIGHT - 1;
493 }
494 
495 /**
496   Draw the Message box.
497   +-------------------------------------------+
498   |            ERROR/WARNING/INFO             |
499   |-------------------------------------------|
500   |              popup messages               |
501   |                                           |
502   |          user selectable options          |
503   +-------------------------------------------+
504 
505   @param  PopupStyle   Popup style to use.
506 
507 **/
508 EFI_STATUS
DrawMessageBox(IN EFI_HII_POPUP_STYLE PopupStyle)509 DrawMessageBox (
510   IN  EFI_HII_POPUP_STYLE    PopupStyle
511   )
512 {
513   UINTN             Index;
514   UINTN             Length;
515   UINTN             EndCol;
516   UINTN             TopRow;
517   UINTN             StartCol;
518   UINTN             BottomRow;
519   CHAR16            Character;
520   UINTN             DisplayRow;
521   UINTN             StringIndex;
522   CHAR16            *TempString;
523   CHAR16            *OutputString;
524   UINTN             ColDimension;
525   UINTN             OutputStrWidth;
526   UINTN             DrawMesStrRowNum;
527 
528   EndCol = gPopupDimensions.RightColumn;
529   TopRow = gPopupDimensions.TopRow;
530   StartCol = gPopupDimensions.LeftColumn;
531   BottomRow = gPopupDimensions.BottomRow;
532   ColDimension = EndCol - StartCol + 1;
533   DrawMesStrRowNum = 0;
534 
535   //
536   // 1. Draw the top of the message box.
537   //
538   Character = BOXDRAW_DOWN_RIGHT;
539   PrintCharAt (StartCol, TopRow, Character);
540   Character = BOXDRAW_HORIZONTAL;
541   for (Index = StartCol; Index + 1 < EndCol; Index++) {
542     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
543   }
544   Character = BOXDRAW_DOWN_LEFT;
545   PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
546 
547   //
548   // 2. Draw the prompt string for different popup styles.
549   //
550   Character = BOXDRAW_VERTICAL;
551   DisplayRow = TopRow + POPUP_BORDER;
552   ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());
553   PrintCharAt (StartCol, DisplayRow, Character);
554   PrintCharAt (EndCol, DisplayRow, Character);
555   if (PopupStyle == EfiHiiPopupStyleError) {
556     PrintStringAt ((ColDimension - (GetStringWidth (gErrorPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gErrorPopup);
557   } else if (PopupStyle == EfiHiiPopupStyleWarning) {
558     PrintStringAt ((ColDimension - (GetStringWidth (gWarningPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gWarningPopup);
559   } else {
560     PrintStringAt ((ColDimension - (GetStringWidth (gInfoPopup) - 2) / 2) / 2 + StartCol, DisplayRow, gInfoPopup);
561   }
562 
563   //
564   // 3. Draw the horizontal line below the prompt string for different popup styles.
565   //
566   DisplayRow = TopRow + POPUP_BORDER + POPUP_STYLE_STRING_HEIGHT;
567   ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());
568   Character = BOXDRAW_HORIZONTAL;
569   for (Index = StartCol + 1; Index < EndCol; Index++) {
570     PrintCharAt (Index, DisplayRow, Character);
571   }
572   Character = BOXDRAW_VERTICAL;
573   PrintCharAt (StartCol, DisplayRow, Character);
574   PrintCharAt (EndCol, DisplayRow, Character);
575 
576   //
577   // 4. Draw the mesage string.
578   //
579   DisplayRow = TopRow + POPUP_HEADER_HEIGHT;
580   for (Index = DisplayRow ,StringIndex = 0; ParseMessageString (gMessageString, &OutputString, &OutputStrWidth,  &StringIndex) != 0 && DrawMesStrRowNum < gMesStrLineNum;) {
581     ClearLines (StartCol,  EndCol, Index, Index, GetPopupColor ());
582     PrintCharAt (StartCol, Index, Character);
583     PrintCharAt (EndCol, Index, Character);
584     if (OutputStrWidth > gMaxRowWidth) {
585       //
586       //OutputStrWidth > MaxMesStrWidth, cut off the string and print print ... instead.
587       //
588       GetStringOffsetWithWidth (OutputString, gMaxRowWidth, &Length);
589       TempString = AllocateZeroPool ((Length + 1) * sizeof (CHAR16));
590       if (TempString == NULL) {
591         FreePool (OutputString);
592         return EFI_OUT_OF_RESOURCES;
593       }
594       StrnCpyS (TempString, Length + 1, OutputString, Length - 3);
595       StrCatS (TempString, Length + 1, L"...");
596       PrintStringAt ((ColDimension - gMaxRowWidth) / 2 + StartCol, Index, TempString);
597       FreePool (TempString);
598     } else {
599       PrintStringAt ((ColDimension - OutputStrWidth) / 2 + StartCol, Index, OutputString);
600     }
601     Index ++;
602     DrawMesStrRowNum ++;
603     FreePool (OutputString);
604   }
605 
606   //
607   // 5. Draw an empty line after message string.
608   //
609   ClearLines (StartCol,  EndCol, Index, Index, GetPopupColor ());
610   PrintCharAt (StartCol, Index, Character);
611   PrintCharAt (EndCol, Index, Character);
612   //
613   // Check whether the actual string row number beyond the MesStrRowNum, if yes, print the ...... in the row.
614   //
615   if (OutputStrWidth > 0 && DrawMesStrRowNum >= gMesStrLineNum) {
616     PrintStringAt ((ColDimension - StrLen (L"......")) / 2 + StartCol, Index, L"......");
617   }
618 
619   //
620   // 6. Draw an empty line which is used to show user selectable options, will draw concrete option strings in function GetUserSelection().
621   //
622   Character = BOXDRAW_VERTICAL;
623   DisplayRow = BottomRow - POPUP_BORDER;
624   ClearLines (StartCol,  EndCol, DisplayRow, DisplayRow, GetPopupColor ());
625   PrintCharAt (StartCol, DisplayRow, Character);
626   PrintCharAt (EndCol, DisplayRow, Character);
627 
628   //
629   // 7. Draw the bottom of the message box.
630   //
631   Character = BOXDRAW_UP_RIGHT;
632   PrintCharAt (StartCol, BottomRow, Character);
633   Character = BOXDRAW_HORIZONTAL;
634   for (Index = StartCol; Index + 1 < EndCol; Index++) {
635     PrintCharAt ((UINTN)-1, (UINTN) -1, Character);
636   }
637   Character = BOXDRAW_UP_LEFT;
638   PrintCharAt ((UINTN)-1, (UINTN) -1, Character);
639 
640   return EFI_SUCCESS;
641 }
642 
643 /**
644   Displays a popup window.
645 
646   @param  This           A pointer to the EFI_HII_POPUP_PROTOCOL instance.
647   @param  PopupStyle     Popup style to use.
648   @param  PopupType      Type of the popup to display.
649   @param  HiiHandle      HII handle of the string pack containing Message
650   @param  Message        A message to display in the popup box.
651   @param  UserSelection  User selection.
652 
653   @retval EFI_SUCCESS            The popup box was successfully displayed.
654   @retval EFI_INVALID_PARAMETER  HiiHandle and Message do not define a valid HII string.
655   @retval EFI_INVALID_PARAMETER  PopupType is not one of the values defined by this specification.
656   @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to display the popup box.
657 
658 **/
659 EFI_STATUS
660 EFIAPI
CreatePopup(IN EFI_HII_POPUP_PROTOCOL * This,IN EFI_HII_POPUP_STYLE PopupStyle,IN EFI_HII_POPUP_TYPE PopupType,IN EFI_HII_HANDLE HiiHandle,IN EFI_STRING_ID Message,OUT EFI_HII_POPUP_SELECTION * UserSelection OPTIONAL)661 CreatePopup (
662   IN  EFI_HII_POPUP_PROTOCOL  *This,
663   IN  EFI_HII_POPUP_STYLE     PopupStyle,
664   IN  EFI_HII_POPUP_TYPE      PopupType,
665   IN  EFI_HII_HANDLE          HiiHandle,
666   IN  EFI_STRING_ID           Message,
667   OUT EFI_HII_POPUP_SELECTION *UserSelection OPTIONAL
668   )
669 {
670   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;
671   EFI_SIMPLE_TEXT_OUTPUT_MODE      SavedConsoleMode;
672   EFI_STATUS                       Status;
673 
674   if ((PopupType < EfiHiiPopupTypeOk) || (PopupType > EfiHiiPopupTypeYesNoCancel)) {
675     return EFI_INVALID_PARAMETER;
676   }
677 
678   if((HiiHandle == NULL) || (Message == 0)) {
679     return EFI_INVALID_PARAMETER;
680   }
681 
682   gMessageString = HiiGetString (HiiHandle, Message, NULL);
683   if(gMessageString == NULL) {
684     return EFI_INVALID_PARAMETER;
685   }
686 
687   ConOut = gST->ConOut;
688   gMaxRowWidth = 0;
689   gMesStrLineNum = 0;
690 
691   CopyMem (&SavedConsoleMode, ConOut->Mode, sizeof (SavedConsoleMode));
692   ConOut->EnableCursor (ConOut, FALSE);
693   ConOut->SetAttribute (ConOut, GetPopupColor ());
694 
695   CalculatePopupPosition (PopupType, &gPopupDimensions);
696 
697   Status = DrawMessageBox (PopupStyle);
698   if (EFI_ERROR (Status)) {
699     goto Done;
700   }
701 
702   //
703   // Add user selectable options to option list: gUserSelectableOptions
704   //
705   Status = AddUserSelectableOptions (PopupType);
706   if (EFI_ERROR (Status)) {
707     goto Done;
708   }
709 
710   GetUserSelection (PopupType, UserSelection);
711 
712 Done:
713   //
714   // Restore Conout attributes and free the resources allocate before.
715   //
716   ConOut->EnableCursor (ConOut, SavedConsoleMode.CursorVisible);
717   ConOut->SetCursorPosition (ConOut, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);
718   ConOut->SetAttribute (ConOut, SavedConsoleMode.Attribute);
719   FreeSelectableOptions (&gUserSelectableOptions);
720   FreePool (gMessageString);
721 
722   return Status;
723 }
724 
725