/* Relay -- a tool to record and play Quake2 demos Copyright (C) 2000 Conor Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Conor Davis cedavis@planetquake.com */ #include #include #include #include #include "shared.h" #include "mem.h" #include "menu.h" #include "utils.h" #define MAX_MENU_ITEMS 128 static menuitem_t temp_items[MAX_MENU_ITEMS]; static int num_items; void Menu_Close(menu_t **head) { menu_t *menu; int i; if (!*head) return; menu = *head; *head = menu->next; if (menu->Close) menu->Close(menu); // free contents if (menu->items) { for (i = 0; i < menu->num; i++) { if (menu->items[i].text) Z_Free(menu->items[i].text); if (menu->items[i].param) Z_Free(menu->items[i].param); } Z_Free(menu->items); } if (menu->param) Z_Free(menu->param); Z_Free(menu); } void Menu_CloseAll(menu_t **head) { while (*head) Menu_Close(head); } int Menu_AddItem(const char *text, const char *fmt, ...) { menuitem_t *item; va_list argptr; int i; item = &temp_items[num_items++]; memset(item, 0, sizeof(menuitem_t)); item->text = Z_Strdup(text); item->numparams = 0; if (fmt == NULL) return num_items - 1; va_start(argptr, fmt); while (*fmt) { if (*fmt >= '0' && *fmt <= '9') { if (!item->param) { while (*fmt >= '0' && *fmt <= '9') { item->numparams = (item->numparams * 10) + *fmt - '0'; fmt++; } item->param = Z_Malloc(item->numparams*sizeof(int)); for (i = 0; i < item->numparams; i++) item->param[i] = va_arg(argptr, int); } else fmt++; } else { switch(toupper(*fmt)) { case 'A': item->align = va_arg(argptr, int); break; case 'I': item->indent = va_arg(argptr, int); break; case 'S': item->Select = va_arg(argptr, void *); break; default: Com_Printf("Menu_AddItem: unknown option '%c'\n", *fmt); } fmt++; } } va_end(argptr); return num_items - 1; } void Menu_Display(menu_t *menu, char *layout, size_t len) { int x, y, i, num; menuitem_t *item; layout[0] = 0; if (!menu) return; // title if (menu->title && menu->title[0]) strcatf(layout, len, "yv 32 xv %d string2 \"%s\" ", 144 - strlen(menu->title)*4, menu->title); if (menu->cur != -1) { if (menu->top > menu->cur) menu->top = menu->cur; if (menu->top < menu->cur - 9) menu->top = menu->cur - 9; if (menu->top > menu->num - 10) menu->top = menu->num - 10; if (menu->top < 0) menu->top = 0; } // last chance to stop messups if (menu->cur != -1) { if (menu->cur >= menu->num || !menu->items[menu->cur].Select) menu->cur = -1; } if (menu->top > 0) strcatf(layout, len, "yv 48 xv 0 string2 \"(Up)\" "); num = 0; y = 56; for (i = 0; i < 10; i++) { if (menu->top + i >= menu->num) break; item = &menu->items[menu->top + i]; switch(item->align) { case MENU_ALIGN_LEFT: x = 0; break; case MENU_ALIGN_CENTER: x = 98 - strlen(item->text)*4; break; case MENU_ALIGN_RIGHT: x = 196 - strlen(item->text)*8; break; default: x = 0; // shutup compiler } x += item->indent; if (item->Select) { num++; if (menu->cur == -1) menu->cur = menu->top + i; if (menu->cur == menu->top + i) strcatf(layout, len, "yv %d xv %d string2 \"%d %s\" ", y, x, num % 10, item->text); else strcatf(layout, len, "yv %d xv %d string \"%d %s\" ", y, x, num % 10, item->text); } else strcatf(layout, len, "yv %d xv %d string \"%s\" ", y, x+16, item->text); y += 8; } if (menu->top + 10 < menu->num) strcatf(layout, len, "yv 136 xv 0 string2 \"(Down)\" "); strcatf(layout, len, "yv 152 xv 0 string2 \"[ ] move cursor up/down\" "); strcatf(layout, len, "yv 160 string2 \"Enter to select; ' to close\" "); strcatf(layout, len, "yv 168 string2 \"F1 for help\" "); } void Menu_Update(menu_t **head, char *layout, size_t len, int id) { menu_t *menu; for (menu = *head; menu; menu = menu->next) { if (menu->id == id) { menu->Show(menu); // in case the menu was closed if (!*head) return; // check if the client is actively viewing the menu if (menu == *head) Menu_Display(menu, layout, len); } } } void Menu_Prev(menu_t *menu) { int i; if (!menu) return; for (i = menu->cur - 1; i >= 0; i--) { if (menu->items[i].Select) { menu->cur = i; return; } } for (i = menu->num - 1; i > menu->cur; i--) { if (menu->items[i].Select) { menu->cur = i; return; } } } void Menu_Next(menu_t *menu) { int i; if (!menu) return; for (i = menu->cur + 1; i < menu->num; i++) { if (menu->items[i].Select) { menu->cur = i; return; } } for (i = 0; i < menu->cur; i++) { if (menu->items[i].Select) { menu->cur = i; return; } } } void Menu_Select(menu_t *menu, int key) { if (!menu) return; if (menu->cur < 0 || menu->cur >= menu->num) return; if (!menu->items[menu->cur].Select) return; menu->items[menu->cur].Select(menu, &menu->items[menu->cur], key); } void Menu_Start(menu_t *menu) { num_items = 0; if (menu->items) { Z_Free(menu->items); menu->items = NULL; } } void Menu_Finish(menu_t *menu) { menu->items = Z_Malloc(num_items*sizeof(menuitem_t)); memcpy(menu->items, temp_items, num_items*sizeof(menuitem_t)); menu->num = num_items; } void Menu_Open(void *client, menu_t **head, void (*Show)(menu_t *), const char *fmt, ...) { menu_t *menu; va_list argptr; int i; if (!fmt) return; menu = Z_Malloc(sizeof(menu_t)); memset(menu, 0, sizeof(menu_t)); menu->cur = -1; menu->client = client; menu->numparams = 0; va_start(argptr, fmt); while (*fmt) { if (!menu->param && isdigit(*fmt)) { while (isdigit(*fmt)) { menu->numparams = (menu->numparams * 10) + *fmt - '0'; fmt++; } menu->param = Z_Malloc(menu->numparams * sizeof(int)); for (i = 0; i < menu->numparams; i++) menu->param[i] = va_arg(argptr, int); } else { switch(toupper(*fmt)) { case 'T': menu->title = Z_Strdup(va_arg(argptr, char *)); break; case 'I': menu->id = va_arg(argptr, int); break; case 'C': menu->Close = va_arg(argptr, void *); break; default: Com_Printf("Menu_Open: unknown option '%c'\n", *fmt); } } fmt++; } va_end(argptr); menu->next = *head; *head = menu; menu->Show = Show; if (!menu->Show) return; menu->Show(menu); }