cb97c57fe0a082df083e8baf3ecdfdedf538fd67
[reactos.git] / freeldr / freeldr / menu.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1999, 2000, 2001 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 "stdlib.h"
22 #include "tui.h"
23 #include "menu.h"
24 #include "options.h"
25 #include "memory.h"
26
27
28 typedef struct
29 {
30 PUCHAR *MenuItemList;
31 ULONG MenuItemCount;
32 LONG MenuTimeRemaining;
33 ULONG SelectedMenuItem;
34
35 ULONG Left;
36 ULONG Top;
37 ULONG Right;
38 ULONG Bottom;
39
40 } MENU_INFO, *PMENU_INFO;
41
42 VOID CalcMenuBoxSize(PMENU_INFO MenuInfo);
43 VOID DrawMenu(PMENU_INFO MenuInfo);
44 VOID DrawMenuBox(PMENU_INFO MenuInfo);
45 VOID DrawMenuItem(PMENU_INFO MenuInfo, ULONG MenuItemNumber);
46 ULONG ProcessMenuKeyboardEvent(PMENU_INFO MenuInfo);
47
48 extern ULONG nScreenWidth; // Screen Width
49 extern ULONG nScreenHeight; // Screen Height
50
51 extern CHAR cStatusBarFgColor; // Status bar foreground color
52 extern CHAR cStatusBarBgColor; // Status bar background color
53 extern CHAR cBackdropFgColor; // Backdrop foreground color
54 extern CHAR cBackdropBgColor; // Backdrop background color
55 extern CHAR cBackdropFillStyle; // Backdrop fill style
56 extern CHAR cTitleBoxFgColor; // Title box foreground color
57 extern CHAR cTitleBoxBgColor; // Title box background color
58 extern CHAR cMessageBoxFgColor; // Message box foreground color
59 extern CHAR cMessageBoxBgColor; // Message box background color
60 extern CHAR cMenuFgColor; // Menu foreground color
61 extern CHAR cMenuBgColor; // Menu background color
62 extern CHAR cTextColor; // Normal text color
63 extern CHAR cSelectedTextColor; // Selected text color
64 extern CHAR cSelectedTextBgColor; // Selected text background color
65
66 BOOL DisplayMenu(PUCHAR MenuItemList[], ULONG MenuItemCount, ULONG DefaultMenuItem, LONG MenuTimeOut, PULONG SelectedMenuItem)
67 {
68 PUCHAR ScreenBuffer;
69 MENU_INFO MenuInformation;
70 ULONG CurrentClockSecond;
71
72 //
73 // The first thing we need to check is the timeout
74 // If it's zero then don't bother with anything,
75 // just return the default item
76 //
77 if (MenuTimeOut == 0)
78 {
79 if (SelectedMenuItem != NULL)
80 {
81 *SelectedMenuItem = DefaultMenuItem;
82 }
83
84 return TRUE;
85 }
86
87 //
88 // Allocate memory to hold screen contents before menu is drawn
89 //
90 ScreenBuffer = AllocateMemory(4000);
91 if (ScreenBuffer == NULL)
92 {
93 return FALSE;
94 }
95
96 //
97 // Save screen contents to our buffer
98 //
99 SaveScreen(ScreenBuffer);
100
101 //
102 // Setup the MENU_INFO structure
103 //
104 MenuInformation.MenuItemList = MenuItemList;
105 MenuInformation.MenuItemCount = MenuItemCount;
106 MenuInformation.MenuTimeRemaining = MenuTimeOut;
107 MenuInformation.SelectedMenuItem = DefaultMenuItem;
108
109 //
110 // Calculate the size of the menu box
111 //
112 CalcMenuBoxSize(&MenuInformation);
113
114 //
115 // Draw the menu
116 //
117 DrawMenu(&MenuInformation);
118
119 //
120 // Get the current second of time
121 //
122 CurrentClockSecond = getsecond();
123
124 //
125 // Process keys
126 //
127 while (1)
128 {
129 //
130 // Process key presses
131 //
132 if (ProcessMenuKeyboardEvent(&MenuInformation) == KEY_ENTER)
133 {
134 //
135 // If they pressed enter then exit this loop
136 //
137 break;
138 }
139
140 //
141 // Update the date & time
142 //
143 UpdateDateTime();
144
145 if (MenuInformation.MenuTimeRemaining > 0)
146 {
147 if (getsecond() != CurrentClockSecond)
148 {
149 //
150 // Update the time information
151 //
152 CurrentClockSecond = getsecond();
153 MenuInformation.MenuTimeRemaining--;
154
155 //
156 // Update the menu
157 //
158 DrawMenuBox(&MenuInformation);
159 }
160 }
161 else if (MenuInformation.MenuTimeRemaining == 0)
162 {
163 //
164 // A time out occurred, exit this loop and return default OS
165 //
166 break;
167 }
168 }
169
170 //
171 // Update the selected menu item information
172 //
173 if (SelectedMenuItem != NULL)
174 {
175 *SelectedMenuItem = MenuInformation.SelectedMenuItem;
176 }
177
178 return TRUE;
179 }
180
181 VOID CalcMenuBoxSize(PMENU_INFO MenuInfo)
182 {
183 ULONG Idx;
184 ULONG Width;
185 ULONG Height;
186 ULONG Length;
187
188 //
189 // Height is the menu item count plus 2 (top border & bottom border)
190 //
191 Height = MenuInfo->MenuItemCount + 2;
192 Height -= 1; // Height is zero-based
193
194 //
195 // Find the length of the longest string in the menu
196 //
197 Width = 0;
198 for(Idx=0; Idx<MenuInfo->MenuItemCount; Idx++)
199 {
200 Length = strlen(MenuInfo->MenuItemList[Idx]);
201
202 if (Length > Width)
203 {
204 Width = Length;
205 }
206 }
207
208 //
209 // Allow room for left & right borders, plus 8 spaces on each side
210 //
211 Width += 18;
212
213 //
214 // Calculate the menu box area
215 //
216 MenuInfo->Left = (nScreenWidth - Width) / 2;
217 MenuInfo->Right = (MenuInfo->Left) + Width;
218 MenuInfo->Top = (( (nScreenHeight - TITLE_BOX_HEIGHT) - Height) / 2 + 1) + (TITLE_BOX_HEIGHT / 2);
219 MenuInfo->Bottom = (MenuInfo->Top) + Height;
220 }
221
222 VOID DrawMenu(PMENU_INFO MenuInfo)
223 {
224 ULONG Idx;
225
226 //
227 // Draw the menu box
228 //
229 DrawMenuBox(MenuInfo);
230
231 //
232 // Draw each line of the menu
233 //
234 for (Idx=0; Idx<MenuInfo->MenuItemCount; Idx++)
235 {
236 DrawMenuItem(MenuInfo, Idx);
237 }
238 }
239
240 VOID DrawMenuBox(PMENU_INFO MenuInfo)
241 {
242 UCHAR MenuLineText[80];
243 UCHAR TempString[80];
244
245 //
246 // Update the status bar
247 //
248 DrawStatusText(" Use \x18\x19 to select, ENTER to boot.");
249
250 //
251 // Draw the menu box
252 //
253 DrawBox(MenuInfo->Left,
254 MenuInfo->Top,
255 MenuInfo->Right,
256 MenuInfo->Bottom,
257 D_VERT,
258 D_HORZ,
259 FALSE, // Filled
260 TRUE, // Shadow
261 ATTR(cMenuFgColor, cMenuBgColor));
262
263 //
264 // If there is a timeout draw the time remaining
265 //
266 if (MenuInfo->MenuTimeRemaining >= 0)
267 {
268 strcpy(MenuLineText, "[ Time Remaining: ");
269 itoa(MenuInfo->MenuTimeRemaining, TempString, 10);
270 strcat(MenuLineText, TempString);
271 strcat(MenuLineText, " ]");
272
273 DrawText(MenuInfo->Right - strlen(MenuLineText) - 1,
274 MenuInfo->Bottom,
275 MenuLineText,
276 ATTR(cMenuFgColor, cMenuBgColor));
277 }
278 }
279
280 VOID DrawMenuItem(PMENU_INFO MenuInfo, ULONG MenuItemNumber)
281 {
282 ULONG Idx;
283 UCHAR MenuLineText[80];
284 ULONG SpaceTotal;
285 ULONG SpaceLeft;
286 ULONG SpaceRight;
287
288 //
289 // We will want the string centered so calculate
290 // how many spaces will be to the left and right
291 //
292 SpaceTotal = (MenuInfo->Right - MenuInfo->Left - 2) - strlen(MenuInfo->MenuItemList[MenuItemNumber]);
293 SpaceLeft = (SpaceTotal / 2) + 1;
294 SpaceRight = (SpaceTotal - SpaceLeft) + 1;
295
296 //
297 // Insert the spaces on the left
298 //
299 for (Idx=0; Idx<SpaceLeft; Idx++)
300 {
301 MenuLineText[Idx] = ' ';
302 }
303 MenuLineText[Idx] = '\0';
304
305 //
306 // Now append the text string
307 //
308 strcat(MenuLineText, MenuInfo->MenuItemList[MenuItemNumber]);
309
310 //
311 // Now append the spaces on the right
312 //
313 for (Idx=0; Idx<SpaceRight; Idx++)
314 {
315 strcat(MenuLineText, " ");
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 DrawText(MenuInfo->Left + 1,
325 MenuInfo->Top + 1 + MenuItemNumber,
326 MenuLineText,
327 ATTR(cSelectedTextColor, cSelectedTextBgColor));
328 }
329 else
330 {
331 DrawText(MenuInfo->Left + 1,
332 MenuInfo->Top + 1 + MenuItemNumber,
333 MenuLineText,
334 ATTR(cTextColor, cMenuBgColor));
335 }
336 }
337
338 ULONG ProcessMenuKeyboardEvent(PMENU_INFO MenuInfo)
339 {
340 ULONG KeyEvent = 0;
341
342 //
343 // Check for a keypress
344 //
345 if (kbhit())
346 {
347 //
348 // Cancel the timeout
349 //
350 if (MenuInfo->MenuTimeRemaining != -1)
351 {
352 MenuInfo->MenuTimeRemaining = -1;
353 DrawMenuBox(MenuInfo);
354 }
355
356 //
357 // Get the key
358 //
359 KeyEvent = getch();
360
361 //
362 // Is it extended?
363 //
364 if (KeyEvent == 0)
365 KeyEvent = getch(); // Yes - so get the extended key
366
367 //
368 // Process the key
369 //
370 switch (KeyEvent)
371 {
372 case KEY_UP:
373
374 if (MenuInfo->SelectedMenuItem > 0)
375 {
376 MenuInfo->SelectedMenuItem--;
377
378 //
379 // Update the menu
380 //
381 DrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem + 1); // Deselect previous item
382 DrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem); // Select new item
383 }
384
385 break;
386
387 case KEY_DOWN:
388
389 if (MenuInfo->SelectedMenuItem < (MenuInfo->MenuItemCount - 1))
390 {
391 MenuInfo->SelectedMenuItem++;
392
393 //
394 // Update the menu
395 //
396 DrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem - 1); // Deselect previous item
397 DrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem); // Select new item
398 }
399
400 break;
401 }
402 }
403
404 return KeyEvent;
405 }