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