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