Move freeldr to reactos\boot\freeldr.
[reactos.git] / reactos / boot / freeldr / freeldr / ui / tuimenu.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <freeldr.h>
21 #include <rtl.h>
22 #include <ui.h>
23 #include "tui.h"
24 #include "keycodes.h"
25 #include <options.h>
26 #include <mm.h>
27 #include <machine.h>
28 #include <video.h>
29
30
31 BOOL TuiDisplayMenu(PUCHAR MenuItemList[], U32 MenuItemCount, U32 DefaultMenuItem, S32 MenuTimeOut, U32* SelectedMenuItem, BOOL CanEscape, UiMenuKeyPressFilterCallback KeyPressFilter)
32 {
33 TUI_MENU_INFO MenuInformation;
34 U32 LastClockSecond;
35 U32 CurrentClockSecond;
36 U32 KeyPress;
37
38 //
39 // The first thing we need to check is the timeout
40 // If it's zero then don't bother with anything,
41 // just return the default item
42 //
43 if (MenuTimeOut == 0)
44 {
45 if (SelectedMenuItem != NULL)
46 {
47 *SelectedMenuItem = DefaultMenuItem;
48 }
49
50 return TRUE;
51 }
52
53 //
54 // Setup the MENU_INFO structure
55 //
56 MenuInformation.MenuItemList = MenuItemList;
57 MenuInformation.MenuItemCount = MenuItemCount;
58 MenuInformation.MenuTimeRemaining = MenuTimeOut;
59 MenuInformation.SelectedMenuItem = DefaultMenuItem;
60
61 //
62 // Calculate the size of the menu box
63 //
64 TuiCalcMenuBoxSize(&MenuInformation);
65
66 //
67 // Draw the menu
68 //
69 TuiDrawMenu(&MenuInformation);
70
71 //
72 // Get the current second of time
73 //
74 MachRTCGetCurrentDateTime(NULL, NULL, NULL, NULL, NULL, &LastClockSecond);
75
76 //
77 // Process keys
78 //
79 while (1)
80 {
81 //
82 // Process key presses
83 //
84 KeyPress = TuiProcessMenuKeyboardEvent(&MenuInformation, KeyPressFilter);
85 if (KeyPress == KEY_ENTER)
86 {
87 //
88 // If they pressed enter then exit this loop
89 //
90 break;
91 }
92 else if (CanEscape && KeyPress == KEY_ESC)
93 {
94 //
95 // They pressed escape, so just return FALSE
96 //
97 return FALSE;
98 }
99
100 //
101 // Update the date & time
102 //
103 TuiUpdateDateTime();
104
105 VideoCopyOffScreenBufferToVRAM();
106
107 if (MenuInformation.MenuTimeRemaining > 0)
108 {
109 MachRTCGetCurrentDateTime(NULL, NULL, NULL, NULL, NULL, &CurrentClockSecond);
110 if (CurrentClockSecond != LastClockSecond)
111 {
112 //
113 // Update the time information
114 //
115 LastClockSecond = CurrentClockSecond;
116 MenuInformation.MenuTimeRemaining--;
117
118 //
119 // Update the menu
120 //
121 TuiDrawMenuBox(&MenuInformation);
122
123 VideoCopyOffScreenBufferToVRAM();
124 }
125 }
126 else if (MenuInformation.MenuTimeRemaining == 0)
127 {
128 //
129 // A time out occurred, exit this loop and return default OS
130 //
131 break;
132 }
133 }
134
135 //
136 // Update the selected menu item information
137 //
138 if (SelectedMenuItem != NULL)
139 {
140 *SelectedMenuItem = MenuInformation.SelectedMenuItem;
141 }
142
143 return TRUE;
144 }
145
146 VOID TuiCalcMenuBoxSize(PTUI_MENU_INFO MenuInfo)
147 {
148 U32 Idx;
149 U32 Width;
150 U32 Height;
151 U32 Length;
152
153 //
154 // Height is the menu item count plus 2 (top border & bottom border)
155 //
156 Height = MenuInfo->MenuItemCount + 2;
157 Height -= 1; // Height is zero-based
158
159 //
160 // Find the length of the longest string in the menu
161 //
162 Width = 0;
163 for(Idx=0; Idx<MenuInfo->MenuItemCount; Idx++)
164 {
165 Length = strlen(MenuInfo->MenuItemList[Idx]);
166
167 if (Length > Width)
168 {
169 Width = Length;
170 }
171 }
172
173 //
174 // Allow room for left & right borders, plus 8 spaces on each side
175 //
176 Width += 18;
177
178 //
179 // Calculate the menu box area
180 //
181 MenuInfo->Left = (UiScreenWidth - Width) / 2;
182 MenuInfo->Right = (MenuInfo->Left) + Width;
183 MenuInfo->Top = (((UiScreenHeight - TUI_TITLE_BOX_CHAR_HEIGHT) - Height) / 2) + TUI_TITLE_BOX_CHAR_HEIGHT;
184 MenuInfo->Bottom = (MenuInfo->Top) + Height;
185 }
186
187 VOID TuiDrawMenu(PTUI_MENU_INFO MenuInfo)
188 {
189 U32 Idx;
190
191 //
192 // Draw the backdrop
193 //
194 UiDrawBackdrop();
195
196 //
197 // Update the status bar
198 //
199 UiDrawStatusText("Use \x18\x19 to select, then press ENTER.");
200
201 //
202 // Draw the menu box
203 //
204 TuiDrawMenuBox(MenuInfo);
205
206 //
207 // Draw each line of the menu
208 //
209 for (Idx=0; Idx<MenuInfo->MenuItemCount; Idx++)
210 {
211 TuiDrawMenuItem(MenuInfo, Idx);
212 }
213
214 VideoCopyOffScreenBufferToVRAM();
215 }
216
217 VOID TuiDrawMenuBox(PTUI_MENU_INFO MenuInfo)
218 {
219 UCHAR MenuLineText[80];
220 UCHAR TempString[80];
221 U32 Idx;
222
223 //
224 // Draw the menu box
225 //
226 UiDrawBox(MenuInfo->Left,
227 MenuInfo->Top,
228 MenuInfo->Right,
229 MenuInfo->Bottom,
230 D_VERT,
231 D_HORZ,
232 FALSE, // Filled
233 TRUE, // Shadow
234 ATTR(UiMenuFgColor, UiMenuBgColor));
235
236 //
237 // If there is a timeout draw the time remaining
238 //
239 if (MenuInfo->MenuTimeRemaining >= 0)
240 {
241 strcpy(MenuLineText, "[ Time Remaining: ");
242 itoa(MenuInfo->MenuTimeRemaining, TempString, 10);
243 strcat(MenuLineText, TempString);
244 strcat(MenuLineText, " ]");
245
246 UiDrawText(MenuInfo->Right - strlen(MenuLineText) - 1,
247 MenuInfo->Bottom,
248 MenuLineText,
249 ATTR(UiMenuFgColor, UiMenuBgColor));
250 }
251
252 //
253 // Now draw the separators
254 //
255 for (Idx=0; Idx<MenuInfo->MenuItemCount; Idx++)
256 {
257 if (stricmp(MenuInfo->MenuItemList[Idx], "SEPARATOR") == 0)
258 {
259 UiDrawText(MenuInfo->Left, MenuInfo->Top + Idx + 1, "\xC7", ATTR(UiMenuFgColor, UiMenuBgColor));
260 UiDrawText(MenuInfo->Right, MenuInfo->Top + Idx + 1, "\xB6", ATTR(UiMenuFgColor, UiMenuBgColor));
261 }
262 }
263 }
264
265 VOID TuiDrawMenuItem(PTUI_MENU_INFO MenuInfo, U32 MenuItemNumber)
266 {
267 U32 Idx;
268 UCHAR MenuLineText[80];
269 U32 SpaceTotal;
270 U32 SpaceLeft;
271 U32 SpaceRight;
272 UCHAR Attribute;
273
274 //
275 // We will want the string centered so calculate
276 // how many spaces will be to the left and right
277 //
278 SpaceTotal = (MenuInfo->Right - MenuInfo->Left - 2) - strlen(MenuInfo->MenuItemList[MenuItemNumber]);
279 SpaceLeft = (SpaceTotal / 2) + 1;
280 SpaceRight = (SpaceTotal - SpaceLeft) + 1;
281
282 //
283 // Insert the spaces on the left
284 //
285 for (Idx=0; Idx<SpaceLeft; Idx++)
286 {
287 MenuLineText[Idx] = ' ';
288 }
289 MenuLineText[Idx] = '\0';
290
291 //
292 // Now append the text string
293 //
294 strcat(MenuLineText, MenuInfo->MenuItemList[MenuItemNumber]);
295
296 //
297 // Now append the spaces on the right
298 //
299 for (Idx=0; Idx<SpaceRight; Idx++)
300 {
301 strcat(MenuLineText, " ");
302 }
303
304 //
305 // If it is a separator then adjust the text accordingly
306 //
307 if (stricmp(MenuInfo->MenuItemList[MenuItemNumber], "SEPARATOR") == 0)
308 {
309 memset(MenuLineText, 0, 80);
310 memset(MenuLineText, 0xC4, (MenuInfo->Right - MenuInfo->Left - 1));
311 Attribute = ATTR(UiMenuFgColor, UiMenuBgColor);
312 }
313 else
314 {
315 Attribute = ATTR(UiTextColor, UiMenuBgColor);
316 }
317
318 //
319 // If this is the selected menu item then draw it as selected
320 // otherwise just draw it using the normal colors
321 //
322 if (MenuItemNumber == MenuInfo->SelectedMenuItem)
323 {
324 UiDrawText(MenuInfo->Left + 1,
325 MenuInfo->Top + 1 + MenuItemNumber,
326 MenuLineText,
327 ATTR(UiSelectedTextColor, UiSelectedTextBgColor));
328 }
329 else
330 {
331 UiDrawText(MenuInfo->Left + 1,
332 MenuInfo->Top + 1 + MenuItemNumber,
333 MenuLineText,
334 Attribute);
335 }
336 }
337
338 U32 TuiProcessMenuKeyboardEvent(PTUI_MENU_INFO MenuInfo, UiMenuKeyPressFilterCallback KeyPressFilter)
339 {
340 U32 KeyEvent = 0;
341
342 //
343 // Check for a keypress
344 //
345 if (MachConsKbHit())
346 {
347 //
348 // Cancel the timeout
349 //
350 if (MenuInfo->MenuTimeRemaining != -1)
351 {
352 MenuInfo->MenuTimeRemaining = -1;
353 TuiDrawMenuBox(MenuInfo);
354 }
355
356 //
357 // Get the key
358 //
359 KeyEvent = MachConsGetCh();
360
361 //
362 // Is it extended?
363 //
364 if (KeyEvent == 0)
365 KeyEvent = MachConsGetCh(); // Yes - so get the extended key
366
367 //
368 // Call the supplied key filter callback function to see
369 // if it is going to handle this keypress.
370 //
371 if (KeyPressFilter != NULL)
372 {
373 if (KeyPressFilter(KeyEvent))
374 {
375 // It processed the key character
376 TuiDrawMenu(MenuInfo);
377
378 return 0;
379 }
380 }
381
382 //
383 // Process the key
384 //
385 switch (KeyEvent)
386 {
387 case KEY_UP:
388
389 if (MenuInfo->SelectedMenuItem > 0)
390 {
391 MenuInfo->SelectedMenuItem--;
392
393 //
394 // Update the menu
395 //
396 TuiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem + 1); // Deselect previous item
397
398 // Skip past any separators
399 if (MenuInfo->SelectedMenuItem > 0 && stricmp(MenuInfo->MenuItemList[MenuInfo->SelectedMenuItem], "SEPARATOR") == 0)
400 {
401 MenuInfo->SelectedMenuItem--;
402 }
403
404 TuiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem); // Select new item
405 }
406
407 break;
408
409 case KEY_DOWN:
410
411 if (MenuInfo->SelectedMenuItem < (MenuInfo->MenuItemCount - 1))
412 {
413 MenuInfo->SelectedMenuItem++;
414
415 //
416 // Update the menu
417 //
418 TuiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem - 1); // Deselect previous item
419
420 // Skip past any separators
421 if (MenuInfo->SelectedMenuItem < (MenuInfo->MenuItemCount - 1) && stricmp(MenuInfo->MenuItemList[MenuInfo->SelectedMenuItem], "SEPARATOR") == 0)
422 {
423 MenuInfo->SelectedMenuItem++;
424 }
425
426 TuiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem); // Select new item
427 }
428
429 break;
430 }
431
432 VideoCopyOffScreenBufferToVRAM();
433 }
434
435 return KeyEvent;
436 }