#include <string.h>
#include <ctype.h>
#include "client.h"
#include "qmenu.h"
static void Action_DoEnter(menuaction_s *a);
static void Action_Draw(menuaction_s *a);
static void Menu_DrawStatusBar(const char *string);
static void Menulist_DoEnter(menulist_s *l);
static void MenuList_Draw(menulist_s *l);
static void Separator_Draw(menuseparator_s *s);
static void Slider_DoSlide(menuslider_s *s, int dir);
static void Slider_Draw(menuslider_s *s);
static void SpinControl_DoEnter(menulist_s *s);
static void SpinControl_Draw(menulist_s *s);
static void SpinControl_DoSlide(menulist_s *s, int dir);
#define RCOLUMN_OFFSET 16
#define LCOLUMN_OFFSET -16
extern refexport_t re;
extern viddef_t viddef;
#define VID_WIDTH viddef.width
#define VID_HEIGHT viddef.height
#define Draw_Char re.DrawChar
#define Draw_Fill re.DrawFill
void Action_DoEnter(menuaction_s *a) {
if(a->generic.callback)
a->generic.callback(a);
}
void Action_Draw(menuaction_s *a) {
if(a->generic.flags & QMF_LEFT_JUSTIFY) {
if(a->generic.flags & QMF_GRAYED)
Menu_DrawStringDark(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y,
a->generic.name);
else
Menu_DrawString(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y,
a->generic.name);
} else {
if(a->generic.flags & QMF_GRAYED)
Menu_DrawStringR2LDark(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y,
a->generic.name);
else
Menu_DrawStringR2L(a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y,
a->generic.name);
}
if(a->generic.ownerdraw)
a->generic.ownerdraw(a);
}
bool Field_DoEnter(menufield_s *f) {
if(f->generic.callback) {
f->generic.callback(f);
return true;
}
return false;
}
void Field_Draw(menufield_s *f) {
int i;
char tempbuffer[128] = "";
if(f->generic.name)
Menu_DrawStringR2LDark(f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y,
f->generic.name);
strncpy(tempbuffer, f->buffer + f->visible_offset, f->visible_length);
Draw_Char(f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18);
Draw_Char(f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24);
Draw_Char(f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4,
20);
Draw_Char(f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4,
26);
for(i = 0; i < f->visible_length; i++) {
Draw_Char(f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19);
Draw_Char(f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25);
}
Menu_DrawString(f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer);
if(Menu_ItemAtCursor(f->generic.parent) == f) {
int offset;
if(f->visible_offset)
offset = f->visible_length;
else
offset = f->cursor;
if(((int)(Sys_Milliseconds() / 250)) & 1) {
Draw_Char(f->generic.x + f->generic.parent->x + (offset + 2) * 8 + 8, f->generic.y + f->generic.parent->y, 11);
} else {
Draw_Char(f->generic.x + f->generic.parent->x + (offset + 2) * 8 + 8, f->generic.y + f->generic.parent->y, ' ');
}
}
}
bool Field_Key(menufield_s *f, int key) {
extern int keydown[];
switch(key) {
case K_KP_SLASH:
key = '/';
break;
case K_KP_MINUS:
key = '-';
break;
case K_KP_PLUS:
key = '+';
break;
case K_KP_HOME:
key = '7';
break;
case K_KP_UPARROW:
key = '8';
break;
case K_KP_PGUP:
key = '9';
break;
case K_KP_LEFTARROW:
key = '4';
break;
case K_KP_5:
key = '5';
break;
case K_KP_RIGHTARROW:
key = '6';
break;
case K_KP_END:
key = '1';
break;
case K_KP_DOWNARROW:
key = '2';
break;
case K_KP_PGDN:
key = '3';
break;
case K_KP_INS:
key = '0';
break;
case K_KP_DEL:
key = '.';
break;
}
if(key > 127) {
switch(key) {
case K_DEL:
default:
return false;
}
}
if((toupper(key) == 'V' && keydown[K_CTRL]) || (((key == K_INS) || (key == K_KP_INS)) && keydown[K_SHIFT])) {
char *cbd;
if((cbd = Sys_GetClipboardData()) != 0) {
strtok(cbd, "\n\r\b");
strncpy(f->buffer, cbd, f->length - 1);
f->cursor = strlen(f->buffer);
f->visible_offset = f->cursor - f->visible_length;
if(f->visible_offset < 0)
f->visible_offset = 0;
free(cbd);
}
return true;
}
switch(key) {
case K_KP_LEFTARROW:
case K_LEFTARROW:
case K_BACKSPACE:
if(f->cursor > 0) {
memmove(&f->buffer[f->cursor - 1], &f->buffer[f->cursor], strlen(&f->buffer[f->cursor]) + 1);
f->cursor--;
if(f->visible_offset) {
f->visible_offset--;
}
}
break;
case K_KP_DEL:
case K_DEL:
memmove(&f->buffer[f->cursor], &f->buffer[f->cursor + 1], strlen(&f->buffer[f->cursor + 1]) + 1);
break;
case K_KP_ENTER:
case K_ENTER:
case K_ESCAPE:
case K_TAB:
return false;
case K_SPACE:
default:
if(!isdigit(key) && (f->generic.flags & QMF_NUMBERSONLY))
return false;
if(f->cursor < f->length) {
f->buffer[f->cursor++] = key;
f->buffer[f->cursor] = 0;
if(f->cursor > f->visible_length) {
f->visible_offset++;
}
}
}
return true;
}
void Menu_AddItem(menuframework_s *menu, void *item) {
if(menu->nitems == 0)
menu->nslots = 0;
if(menu->nitems < MAXMENUITEMS) {
menu->items[menu->nitems] = item;
((menucommon_s *)menu->items[menu->nitems])->parent = menu;
menu->nitems++;
}
menu->nslots = Menu_TallySlots(menu);
}
void Menu_AdjustCursor(menuframework_s *m, int dir) {
menucommon_s *citem;
if(m->cursor >= 0 && m->cursor < m->nitems) {
if((citem = Menu_ItemAtCursor(m)) != 0) {
if(citem->type != MTYPE_SEPARATOR)
return;
}
}
if(dir == 1) {
while(1) {
citem = Menu_ItemAtCursor(m);
if(citem)
if(citem->type != MTYPE_SEPARATOR)
break;
m->cursor += dir;
if(m->cursor >= m->nitems)
m->cursor = 0;
}
} else {
while(1) {
citem = Menu_ItemAtCursor(m);
if(citem)
if(citem->type != MTYPE_SEPARATOR)
break;
m->cursor += dir;
if(m->cursor < 0)
m->cursor = m->nitems - 1;
}
}
}
void Menu_Center(menuframework_s *menu) {
int height;
height = ((menucommon_s *)menu->items[menu->nitems - 1])->y;
height += 10;
menu->y = (VID_HEIGHT - height) / 2;
}
void Menu_Draw(menuframework_s *menu) {
int i;
menucommon_s *item;
for(i = 0; i < menu->nitems; i++) {
switch(((menucommon_s *)menu->items[i])->type) {
case MTYPE_FIELD:
Field_Draw((menufield_s *)menu->items[i]);
break;
case MTYPE_SLIDER:
Slider_Draw((menuslider_s *)menu->items[i]);
break;
case MTYPE_LIST:
MenuList_Draw((menulist_s *)menu->items[i]);
break;
case MTYPE_SPINCONTROL:
SpinControl_Draw((menulist_s *)menu->items[i]);
break;
case MTYPE_ACTION:
Action_Draw((menuaction_s *)menu->items[i]);
break;
case MTYPE_SEPARATOR:
Separator_Draw((menuseparator_s *)menu->items[i]);
break;
}
}
item = Menu_ItemAtCursor(menu);
if(item && item->cursordraw) {
item->cursordraw(item);
} else if(menu->cursordraw) {
menu->cursordraw(menu);
} else if(item && item->type != MTYPE_FIELD) {
if(item->flags & QMF_LEFT_JUSTIFY) {
Draw_Char(menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y,
12 + ((int)(Sys_Milliseconds() / 250) & 1));
} else {
Draw_Char(menu->x + item->cursor_offset, menu->y + item->y, 12 + ((int)(Sys_Milliseconds() / 250) & 1));
}
}
if(item) {
if(item->statusbarfunc)
item->statusbarfunc((void *)item);
else if(item->statusbar)
Menu_DrawStatusBar(item->statusbar);
else
Menu_DrawStatusBar(menu->statusbar);
} else {
Menu_DrawStatusBar(menu->statusbar);
}
}
void Menu_DrawStatusBar(const char *string) {
if(string) {
int l = strlen(string);
int maxcol = VID_WIDTH / 8;
int col = maxcol / 2 - l / 2;
Draw_Fill(0, VID_HEIGHT - 8, VID_WIDTH, 8, 4);
Menu_DrawString(col * 8, VID_HEIGHT - 8, string);
} else {
Draw_Fill(0, VID_HEIGHT - 8, VID_WIDTH, 8, 0);
}
}
void Menu_DrawString(int x, int y, const char *string) {
unsigned i;
for(i = 0; i < strlen(string); i++) {
Draw_Char((x + i * 8), y, string[i]);
}
}
void Menu_DrawStringDark(int x, int y, const char *string) {
unsigned i;
for(i = 0; i < strlen(string); i++) {
Draw_Char((x + i * 8), y, string[i] + 128);
}
}
void Menu_DrawStringR2L(int x, int y, const char *string) {
unsigned i;
for(i = 0; i < strlen(string); i++) {
Draw_Char((x - i * 8), y, string[strlen(string) - i - 1]);
}
}
void Menu_DrawStringR2LDark(int x, int y, const char *string) {
unsigned i;
for(i = 0; i < strlen(string); i++) {
Draw_Char((x - i * 8), y, string[strlen(string) - i - 1] + 128);
}
}
void *Menu_ItemAtCursor(menuframework_s *m) {
if(m->cursor < 0 || m->cursor >= m->nitems)
return 0;
return m->items[m->cursor];
}
bool Menu_SelectItem(menuframework_s *s) {
menucommon_s *item = (menucommon_s *)Menu_ItemAtCursor(s);
if(item) {
switch(item->type) {
case MTYPE_FIELD:
return Field_DoEnter((menufield_s *)item);
case MTYPE_ACTION:
Action_DoEnter((menuaction_s *)item);
return true;
case MTYPE_LIST:
return false;
case MTYPE_SPINCONTROL:
return false;
}
}
return false;
}
void Menu_SetStatusBar(menuframework_s *m, const char *string) { m->statusbar = string; }
void Menu_SlideItem(menuframework_s *s, int dir) {
menucommon_s *item = (menucommon_s *)Menu_ItemAtCursor(s);
if(item) {
switch(item->type) {
case MTYPE_SLIDER:
Slider_DoSlide((menuslider_s *)item, dir);
break;
case MTYPE_SPINCONTROL:
SpinControl_DoSlide((menulist_s *)item, dir);
break;
}
}
}
int Menu_TallySlots(menuframework_s *menu) {
int i;
int total = 0;
for(i = 0; i < menu->nitems; i++) {
if(((menucommon_s *)menu->items[i])->type == MTYPE_LIST) {
int nitems = 0;
const char **n = ((menulist_s *)menu->items[i])->itemnames;
while(*n)
nitems++, n++;
total += nitems;
} else {
total++;
}
}
return total;
}
void Menulist_DoEnter(menulist_s *l) {
int start;
start = l->generic.y / 10 + 1;
l->curvalue = l->generic.parent->cursor - start;
if(l->generic.callback)
l->generic.callback(l);
}
void MenuList_Draw(menulist_s *l) {
const char **n;
int y = 0;
Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y,
l->generic.name);
n = l->itemnames;
Draw_Fill(l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue * 10 + 10, 128,
10, 16);
while(*n) {
Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET,
l->generic.y + l->generic.parent->y + y + 10, *n);
n++;
y += 10;
}
}
void Separator_Draw(menuseparator_s *s) {
if(s->generic.name)
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name);
}
void Slider_DoSlide(menuslider_s *s, int dir) {
s->curvalue += dir;
if(s->curvalue > s->maxvalue)
s->curvalue = s->maxvalue;
else if(s->curvalue < s->minvalue)
s->curvalue = s->minvalue;
if(s->generic.callback)
s->generic.callback(s);
}
#define SLIDER_RANGE 10
void Slider_Draw(menuslider_s *s) {
int i;
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET, s->generic.y + s->generic.parent->y,
s->generic.name);
s->range = (s->curvalue - s->minvalue) / (float)(s->maxvalue - s->minvalue);
if(s->range < 0)
s->range = 0;
if(s->range > 1)
s->range = 1;
Draw_Char(s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128);
for(i = 0; i < SLIDER_RANGE; i++)
Draw_Char(RCOLUMN_OFFSET + s->generic.x + i * 8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y,
129);
Draw_Char(RCOLUMN_OFFSET + s->generic.x + i * 8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130);
Draw_Char((int)(8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE - 1) * 8 * s->range),
s->generic.y + s->generic.parent->y, 131);
}
void SpinControl_DoEnter(menulist_s *s) {
s->curvalue++;
if(s->itemnames[s->curvalue] == 0)
s->curvalue = 0;
if(s->generic.callback)
s->generic.callback(s);
}
void SpinControl_DoSlide(menulist_s *s, int dir) {
s->curvalue += dir;
if(s->curvalue < 0)
s->curvalue = 0;
else if(s->itemnames[s->curvalue] == 0)
s->curvalue--;
if(s->generic.callback)
s->generic.callback(s);
}
void SpinControl_Draw(menulist_s *s) {
char buffer[100];
if(s->generic.name) {
Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET, s->generic.y + s->generic.parent->y,
s->generic.name);
}
if(!strchr(s->itemnames[s->curvalue], '\n')) {
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y,
s->itemnames[s->curvalue]);
} else {
strcpy(buffer, s->itemnames[s->curvalue]);
*strchr(buffer, '\n') = 0;
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer);
strcpy(buffer, strchr(s->itemnames[s->curvalue], '\n') + 1);
Menu_DrawString(RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10,
buffer);
}
}