1 /*
2 	Relay -- a tool to record and play Quake2 demos
3 	Copyright (C) 2000 Conor Davis
4 
5 	This program is free software; you can redistribute it and/or
6 	modify it under the terms of the GNU General Public License
7 	as published by the Free Software Foundation; either version 2
8 	of the License, or (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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 	Conor Davis
20 	cedavis@planetquake.com
21 */
22 
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "shared.h"
29 #include "mem.h"
30 #include "menu.h"
31 #include "utils.h"
32 
33 #define MAX_MENU_ITEMS	128
34 
35 static menuitem_t temp_items[MAX_MENU_ITEMS];
36 static int num_items;
37 
Menu_Close(menu_t ** head)38 void Menu_Close(menu_t **head)
39 {
40 	menu_t	*menu;
41 	int 	i;
42 
43 	if (!*head)
44 		return;
45 
46 	menu = *head;
47 	*head = menu->next;
48 
49 	if (menu->Close)
50 		menu->Close(menu);
51 
52 	// free contents
53 	if (menu->items)
54 	{
55 		for (i = 0; i < menu->num; i++)
56 		{
57 			if (menu->items[i].text)
58 				Z_Free(menu->items[i].text);
59 			if (menu->items[i].param)
60 				Z_Free(menu->items[i].param);
61 		}
62 		Z_Free(menu->items);
63 	}
64 	if (menu->param)
65 		Z_Free(menu->param);
66 	Z_Free(menu);
67 }
68 
Menu_CloseAll(menu_t ** head)69 void Menu_CloseAll(menu_t **head)
70 {
71 	while (*head)
72 		Menu_Close(head);
73 }
74 
Menu_AddItem(const char * text,const char * fmt,...)75 int Menu_AddItem(const char *text, const char *fmt, ...)
76 {
77 	menuitem_t	*item;
78 	va_list 	argptr;
79 	int 		i;
80 
81 	item = &temp_items[num_items++];
82 	memset(item, 0, sizeof(menuitem_t));
83 	item->text = Z_Strdup(text);
84 	item->numparams = 0;
85 
86 	if (fmt == NULL)
87 		return num_items - 1;
88 
89 	va_start(argptr, fmt);
90 	while (*fmt)
91 	{
92 		if (*fmt >= '0' && *fmt <= '9')
93 		{
94 			if (!item->param)
95 			{
96 				while (*fmt >= '0' && *fmt <= '9')
97 				{
98 					item->numparams = (item->numparams * 10) + *fmt - '0';
99 					fmt++;
100 				}
101 
102 				item->param = Z_Malloc(item->numparams*sizeof(int));
103 				for (i = 0; i < item->numparams; i++)
104 					item->param[i] = va_arg(argptr, int);
105 			}
106 			else
107 				fmt++;
108 		}
109 		else
110 		{
111 			switch(toupper(*fmt))
112 			{
113 			case 'A':
114 				item->align = va_arg(argptr, int);
115 				break;
116 			case 'I':
117 				item->indent = va_arg(argptr, int);
118 				break;
119 			case 'S':
120 				item->Select = va_arg(argptr, void *);
121 				break;
122 			default:
123 				Com_Printf("Menu_AddItem: unknown option '%c'\n", *fmt);
124 			}
125 
126 			fmt++;
127 		}
128 	}
129 	va_end(argptr);
130 
131 	return num_items - 1;
132 }
133 
Menu_Display(menu_t * menu,char * layout,size_t len)134 void Menu_Display(menu_t *menu, char *layout, size_t len)
135 {
136 	int 		x, y, i, num;
137 	menuitem_t	*item;
138 
139 	layout[0] = 0;
140 	if (!menu)
141 		return;
142 
143 	// title
144 	if (menu->title && menu->title[0])
145 		strcatf(layout, len, "yv 32 xv %d string2 \"%s\" ", 144 - strlen(menu->title)*4, menu->title);
146 
147 	if (menu->cur != -1)
148 	{
149 		if (menu->top > menu->cur)
150 			menu->top = menu->cur;
151 		if (menu->top < menu->cur - 9)
152 			menu->top = menu->cur - 9;
153 
154 		if (menu->top > menu->num - 10)
155 			menu->top = menu->num - 10;
156 		if (menu->top < 0)
157 			menu->top = 0;
158 	}
159 
160 	// last chance to stop messups
161 	if (menu->cur != -1)
162 	{
163 		if (menu->cur >= menu->num || !menu->items[menu->cur].Select)
164 			menu->cur = -1;
165 	}
166 
167 	if (menu->top > 0)
168 		strcatf(layout, len, "yv 48 xv 0 string2 \"(Up)\" ");
169 
170 	num = 0;
171 	y = 56;
172 	for (i = 0; i < 10; i++)
173 	{
174 		if (menu->top + i >= menu->num)
175 			break;
176 
177 		item = &menu->items[menu->top + i];
178 		switch(item->align)
179 		{
180 		case MENU_ALIGN_LEFT:	x = 0; break;
181 		case MENU_ALIGN_CENTER: x = 98 - strlen(item->text)*4; break;
182 		case MENU_ALIGN_RIGHT:	x = 196 - strlen(item->text)*8; break;
183 		default: x = 0; // shutup compiler
184 		}
185 		x += item->indent;
186 
187 		if (item->Select)
188 		{
189 			num++;
190 			if (menu->cur == -1)
191 				menu->cur = menu->top + i;
192 			if (menu->cur == menu->top + i)
193 				strcatf(layout, len, "yv %d xv %d string2 \"%d %s\" ", y, x, num % 10, item->text);
194 			else
195 				strcatf(layout, len, "yv %d xv %d string \"%d %s\" ", y, x, num % 10, item->text);
196 		}
197 		else
198 			strcatf(layout, len, "yv %d xv %d string \"%s\" ", y, x+16, item->text);
199 
200 		y += 8;
201 	}
202 
203 	if (menu->top + 10 < menu->num)
204 		strcatf(layout, len, "yv 136 xv 0 string2 \"(Down)\" ");
205 
206 	strcatf(layout, len, "yv 152 xv 0 string2 \"[ ] move cursor up/down\" ");
207 	strcatf(layout, len, "yv 160 string2 \"Enter to select; ' to close\" ");
208 	strcatf(layout, len, "yv 168 string2 \"F1 for help\" ");
209 }
210 
Menu_Update(menu_t ** head,char * layout,size_t len,int id)211 void Menu_Update(menu_t **head, char *layout, size_t len, int id)
212 {
213 	menu_t	*menu;
214 
215 	for (menu = *head; menu; menu = menu->next)
216 	{
217 		if (menu->id == id)
218 		{
219 			menu->Show(menu);
220 
221 			// in case the menu was closed
222 			if (!*head)
223 				return;
224 
225 			// check if the client is actively viewing the menu
226 			if (menu == *head)
227 				Menu_Display(menu, layout, len);
228 		}
229 	}
230 }
231 
Menu_Prev(menu_t * menu)232 void Menu_Prev(menu_t *menu)
233 {
234 	int i;
235 
236 	if (!menu)
237 		return;
238 
239 	for (i = menu->cur - 1; i >= 0; i--)
240 	{
241 		if (menu->items[i].Select)
242 		{
243 			menu->cur = i;
244 			return;
245 		}
246 	}
247 
248 	for (i = menu->num - 1; i > menu->cur; i--)
249 	{
250 		if (menu->items[i].Select)
251 		{
252 			menu->cur = i;
253 			return;
254 		}
255 	}
256 }
257 
Menu_Next(menu_t * menu)258 void Menu_Next(menu_t *menu)
259 {
260 	int 		i;
261 
262 	if (!menu)
263 		return;
264 
265 	for (i = menu->cur + 1; i < menu->num; i++)
266 	{
267 		if (menu->items[i].Select)
268 		{
269 			menu->cur = i;
270 			return;
271 		}
272 	}
273 
274 	for (i = 0; i < menu->cur; i++)
275 	{
276 		if (menu->items[i].Select)
277 		{
278 			menu->cur = i;
279 			return;
280 		}
281 	}
282 }
283 
Menu_Select(menu_t * menu,int key)284 void Menu_Select(menu_t *menu, int key)
285 {
286 	if (!menu)
287 		return;
288 
289 	if (menu->cur < 0 || menu->cur >= menu->num)
290 		return;
291 	if (!menu->items[menu->cur].Select)
292 		return;
293 
294 	menu->items[menu->cur].Select(menu, &menu->items[menu->cur], key);
295 }
296 
Menu_Start(menu_t * menu)297 void Menu_Start(menu_t *menu)
298 {
299 	num_items = 0;
300 	if (menu->items)
301 	{
302 		Z_Free(menu->items);
303 		menu->items = NULL;
304 	}
305 }
306 
Menu_Finish(menu_t * menu)307 void Menu_Finish(menu_t *menu)
308 {
309 	menu->items = Z_Malloc(num_items*sizeof(menuitem_t));
310 	memcpy(menu->items, temp_items, num_items*sizeof(menuitem_t));
311 	menu->num = num_items;
312 }
313 
Menu_Open(void * client,menu_t ** head,void (* Show)(menu_t *),const char * fmt,...)314 void Menu_Open(void *client, menu_t **head, void (*Show)(menu_t *), const char *fmt, ...)
315 {
316 	menu_t	*menu;
317 	va_list argptr;
318 	int 	i;
319 
320 	if (!fmt)
321 		return;
322 
323 	menu = Z_Malloc(sizeof(menu_t));
324 	memset(menu, 0, sizeof(menu_t));
325 	menu->cur = -1;
326 	menu->client = client;
327 	menu->numparams = 0;
328 
329 	va_start(argptr, fmt);
330 
331 	while (*fmt)
332 	{
333 		if (!menu->param && isdigit(*fmt))
334 		{
335 			while (isdigit(*fmt))
336 			{
337 				menu->numparams = (menu->numparams * 10) + *fmt - '0';
338 				fmt++;
339 			}
340 
341 			menu->param = Z_Malloc(menu->numparams * sizeof(int));
342 			for (i = 0; i < menu->numparams; i++)
343 				menu->param[i] = va_arg(argptr, int);
344 		}
345 		else
346 		{
347 			switch(toupper(*fmt))
348 			{
349 			case 'T':
350 				menu->title = Z_Strdup(va_arg(argptr, char *));
351 				break;
352 			case 'I':
353 				menu->id = va_arg(argptr, int);
354 				break;
355 			case 'C':
356 				menu->Close = va_arg(argptr, void *);
357 				break;
358 			default:
359 				Com_Printf("Menu_Open: unknown option '%c'\n", *fmt);
360 			}
361 		}
362 
363 		fmt++;
364 	}
365 
366 	va_end(argptr);
367 
368 	menu->next = *head;
369 	*head = menu;
370 
371 	menu->Show = Show;
372 	if (!menu->Show)
373 		return;
374 
375 	menu->Show(menu);
376 }
377