This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/pr_uimenu/mmanager.c

915 lines
19 KiB
C

///////////////////////////////////////////////
// Menu Manager Source File
///////////////////////
// This file belongs to dpmod/darkplaces
// AK contains the manager code
///////////////////////////////////////////////
void(void) menu_init =
{
//registercvar("menu_reloadlist","0");
menu_load();
};
void(void) menu_load =
{
// load the menu files
float i;
for(i = 0; i < NUM_RESFILES; i++ )
{
menu_loadmenu(MENU_FILENAME_LIST[i]);
}
menu_linkwindows();
};
void(string file) menu_addfiletolist =
{
error("menu_addfiletolist called\n");
};
void(void) menu_restart =
{
// actually we empty the ent list and load everything one more time, thats it
entity ent;
float pevused;
string oldpev;
string oldactive;
string oldselected;
// we backup the active window name and the name of the selected item
oldactive = menu_activewindow.name;
oldselected = menu_selected.name;
// backup pev's name
if(pev != null_entity)
{
oldpev = pev.name;
pevused = true;
}
else
{
pevused = false;
}
// first clear the history
menu_clearhistory();
// we remove all items
ent = null_entity;
while((ent = nextent(ent)) != null_entity)
{
menu_removeitem(ent);
}
// FIXME: here's a "little" hack (so PRVM_ED_Alloc replaces the items instead of creating new ones)
time = time + 1.0;
// now call menu_load
menu_load();
time = time - 1.0; // we cant use gettime cause that always returns time :-/
// perform the init
menu_performreinit();
ent = menu_getitem(oldactive);
if(ent)
{
menu_activewindow = ent;
ent = menu_getitem(oldselected);
if(ent)
{
menu_selected = ent;
}
}
if(pevused)
{
ent = menu_getitem(oldpev);
if(ent)
{
pev = ent;
} else // we have no current pev...
{
error("Reloaded menu files, but the former pev (", oldpev ,") item is missing !\n");
}
}
};
void menu_loadmenu (string file)
{
if(loadfromfile(file) == -1)
{
dprint("^3Warning:^7 couldn't load ", file, "\n");
}
}
entity(entity start, .entity find1, entity match, .float find2, float match2) findef =
{
while(1)
{
start = findentity(start, find1, match);
if(start == null_entity)
break;
if(start.find2 == match2)
break;
}
return start;
/*while((start = findentity(start,find1,match))!=null_entity)
if(start.find2 == match2)
break;
return start;*/
};
void(void) menu_linkwindows =
{
// first verify that MENU_NORMAL_NAME and MENU_INGAME_NAME exist
// if not add the default strings
entity ent;
float x, opos;
ent = findstring(null_entity, name, MENU_NORMAL_NAME);
if(ent == null_entity) loadfromdata(MENU_NORMAL_DEFAULT);
// verify again if MENI_INGAME_NAME is there now
ent = findstring(null_entity,name, MENU_NORMAL_NAME);
if(ent == null_entity) error("Bad MENU_NORMAL_DEFAULT !\n");
ent = findstring(null_entity,name, MENU_INGAME_NAME);
if(ent == null_entity) loadfromdata(MENU_INGAME_DEFAULT);
// verify again if MENI_INGAME_NAME is there now
ent = findstring(null_entity, name, MENU_INGAME_NAME);
if(ent == null_entity) error("Bad MENU_INGAME_DEFAULT !\n");
// verify that every name is only used *once*
ent = null_entity;
while((ent = nextent(ent)) != null_entity)
{
pev = ent;
while((pev = findstring(pev, name, ent.name)) != null_entity)
{
if(pev != null_entity)
{
objerror("Name ", ent.name, " already used !\n");
}
}
}
// now we have to :
// set the parent field with parent_name
// set the next and prev fields
// set the child field
pev = null_entity;
while((pev = nextent(pev)) != null_entity)
{
if(pev.name == "")
{
objerror("Name is missing !\n");
continue;
}
if(pev.type == "")
{
objerror("Type is missing !\n");
continue;
}
if(!isfunction(pev.type))
{
objerror("Control ", pev.type, " not found !\n");
continue;
}
// find parent
// if parent_name is "" do nothing else set parent
if(pev.parent != "")
{
ent = findstring(null_entity, name, pev.parent);
if(ent == null_entity)
{
objerror("Item ", pev.parent, " not found !\n");
continue;
}
pev._parent = ent;
}
else
{
pev._parent = null_entity;
}
}
// now auto-set all ents with orderpos 0
pev = null_entity;
while((pev = findfloat(pev,orderpos, 0)) != null_entity)
{
if(pev.parent == "")
continue;
// now go through all orderpos' beginning from 1
opos = 1;
while((ent = findef(null_entity, _parent, pev._parent, orderpos, opos)) != null_entity)
{
opos = opos + 1;
}
pev.orderpos = opos;
}
pev = null_entity;
while((pev = nextent(pev)) != null_entity)
{
// find first child
// orderpos starts with 1
ent = findef(null_entity, _parent, pev, orderpos, 1);
if(ent == null_entity)
{
if(findentity(ent, _parent, pev) != null_entity)
{
objerror("Order pos 1 is missing in the child list of ", pev.name, " !\n");
continue;
}
//else doesnt have any chilren
}
else
pev._child = ent;
// add to next, previous list
// find orderpos - x (starting with x = 1)
x = pev.orderpos;
while(x > 1)
{
x = x - 1;
ent = findef(null_entity, _parent, pev._parent, orderpos, x);
if(ent != null_entity)
{
pev._prev = ent;
ent._next = pev;
break;
}
}
// find orderpos + x (starting with x = 1 until x == pev.oderpos + 100)
x = pev.orderpos;
while(x < pev.orderpos + 100)
{
x = x + 1;
ent = findef(null_entity, _parent, pev._parent, orderpos, x);
if(ent != null_entity)
{
pev._next = ent;
ent._prev = pev;
break;
}
}
}
// call the type functions (former classname functions)
ent = null_entity;
while((ent = nextent(ent)) != null_entity)
{
pev = ent;
callfunction(pev.type);
}
};
void(void) menu_toggle =
{
// only let the qc toggle the menu if we are ingame or a developer
if(gamestatus & GAME_CONNECTED || cvar("developer"))
{
// then allow toggling
m_toggle();
}// else do nothing
};
void(void) menu_performreinit =
{
// clear history
menu_clearhistory();
// and reinit all menu items
pev = null_entity;
while((pev = nextent(pev)) != null_entity)
{
if(pev.parent == "")
pev._parent = null_entity;
//else actually this shouldnt happen
else if(pev._parent.name != pev.parent)
objerror("Parent (should be ", pev.parent, ") of non-menu item ", pev.name, " changed to ", pev._parent.name, " !\n");
raise_reinit(pev); // always call reinit
}
// choose which menu to display
if(MENU_ALLOWINGAME && (gamestatus & GAME_CONNECTED))
menu_activewindow = findstring(null_entity, name, MENU_INGAME_NAME);
else menu_activewindow = findstring(null_entity, name, MENU_NORMAL_NAME);
// set the selected item
menu_selected = menu_activewindow;
// find first child that can be selected
menu_selectdown();
};
void(entity par, float selectalways) menu_processmouse =
{
// pev is parent
// loop through all childs
// and try to find an object whose click rect fits to the mouse coords
entity ent;
vector old_cursor, local_cursor;
ent = par._child;
if(ent == null_entity)
return;
menu_localorigin = menu_localorigin + par.origin;
old_cursor = menu_cursor;
local_cursor = gfx_congfxtomen(cursor);
do
{
// if not visible, continue to the next item
if(menu_isvisible(ent))
{
old_cursor = menu_cursor;
menu_cursor = local_cursor;
if(inrect(local_cursor, ent.click_pos, ent.click_size))
{
// call mouse_enter event ?
if(!(ent.flag & _FLAG_MOUSEINAREA) && menu_hasevents(ent))
{
raise_mouse_enter(ent);
ent.flag = ent.flag | _FLAG_MOUSEINAREA;
}
// select it ?
if(menu_selectable(ent))
{
menu_selected = ent;
}
else if(selectalways)
{
if(menu_hasevents(ent))
{
menu_selected = ent;
}
}
}
else
{
// call mouse_leave event ?
if((ent.flag & _FLAG_MOUSEINAREA) && menu_hasevents(ent))
{
raise_mouse_leave(ent);
// this only works if _FLAG_MOUSEINAREA is set
ent.flag = ent.flag - _FLAG_MOUSEINAREA;
}
}
if(menu_selected != ent)
menu_cursor = old_cursor;
// if ent has children recurse through them
if(ent._child != null_entity)
{
menu_processmouse(ent, selectalways);
}
}
} while((ent = ent._next) != null_entity);
menu_localorigin = menu_localorigin - par.origin;
};
void(void) menu_frame =
{
/*
// this is only for debugging purposes
// thus its unstable and *won't* work any more when Ive changed dp's behavior with
// the builtin list (the precache functions will only work for menu_init)
if(cvar("menu_reloadlist"))
{
cvar_set("menu_reloadlist","0");
menu_restart();
}*/
// if mouse moved, process it
if(cursor_rel != '0 0 0')
menu_processmouse(menu_activewindow, false);
}
void(entity menu) menu_drawwindow =
{
// loop through all children and draw them
entity ent;
vector old_c_pos, old_c_size, clipped_size;
// set the clipping area
// is this necessary at all ?
if(menu_clip_size != '0 0 0')
{
clipped_size = cliprectsize(gfx_conmentogfx(menu.clip_pos),menu.clip_size, menu_clip_pos, menu_clip_size);
}
else
{
clipped_size = menu.clip_size;
}
if(clipped_size != '0 0 0')
{
// do clip the clip area
// save the old
old_c_pos = menu_clip_pos;
old_c_size = menu_clip_size;
// clip the position
menu_clip_pos = cliprectpos( gfx_conmentogfx(menu.clip_pos) , menu.clip_size, menu_clip_pos, menu_clip_size);
menu_clip_size = clipped_size;
gfx_setcliparea(menu_clip_pos, menu_clip_size);
}
// set the localorigin (the clipping position wont be affected)
menu_localorigin = menu_localorigin + menu.origin;
ent = menu._child;
do
{
// if it's not visible continue
if(menu_isvisible(ent))
{
pev = ent;
if(menu_hasevents(ent))
{
raise_refresh(ent);
} else if(ent.flag & FLAG_DRAWREFRESHONLY)
{
raise_refresh(ent);
} else if(ent._parent)
{
if(ent._parent.flag & FLAG_CHILDDRAWREFRESHONLY)
raise_refresh(ent);
}
raise_draw(ent);
if(ent._child != null_entity)
{
menu_drawwindow(ent);
}
// reset the clip area to the current menu's
if(menu_clip_size != '0 0 0')
gfx_setcliparea(menu_clip_pos, menu_clip_size);
else
gfx_resetcliparea();
}
} while((ent = ent._next) != null_entity);
menu_localorigin = menu_localorigin - menu.origin;
// restore the old menu_clip vars if necessary
if(clipped_size != '0 0 0')
{
menu_clip_size = old_c_size;
menu_clip_pos = old_c_pos;
}
};
void(void) menu_draw =
{
// if menu_activewindow is visible loop though it
if(menu_isvisible(menu_activewindow))
{
menu_drawwindow(menu_activewindow);
menu_localorigin = '0 0 0';
menu_clip_pos = '0 0 0';
menu_clip_size = '0 0 0';
menu_resetcliparea();
}
}
float(entity e) menu_hasevents =
{
if(e.flag & FLAG_DRAWONLY)
return false;
if(e.flag & FLAG_DRAWREFRESHONLY)
return false;
if(e._parent)
{
if(e._parent.flag & FLAG_CHILDDRAWONLY)
return false;
if(e._parent.flag & FLAG_CHILDDRAWREFRESHONLY)
return false;
}
if(menu_isvisible(e))
return true;
return false;
};
float(entity e) menu_isvisible =
{
if(e.flag & FLAG_HIDDEN)
return false;
if((e.flag & FLAG_SERVERONLY) && !(gamestatus & GAME_ISSERVER))
return false;
if((e.flag & FLAG_DEVELOPERONLY) && !(gamestatus & GAME_DEVELOPER))
return false;
return true;
};
float(entity e) menu_selectable =
{
if(!menu_hasevents(e))
return false;
if(e.flag & FLAG_NOSELECT)
return false;
//if(e == menu_getitem("quit"))
// crash();
return true;
};
void(void) menu_shutdown =
{
// call the terminate event for each object
pev = null_entity;
while((pev = nextent(pev)) != null_entity)
{
raise_destroy(pev);
}
};
void(float keynr, string ascii) menu_keydown =
{
// before calling the current keydown functions, process the mouse again
// so only the correct item is called
// (except mouse wheel up and down)
// if the mouse doesnt point to an item, there wont be a reaction on the clicking
if(K_MOUSE1 <= keynr && keynr <= K_MOUSE3)
{
entity key_selected;
key_selected = menu_selected;
menu_selected = null_entity;
menu_processmouse(menu_activewindow, true);
// if we find anything, we give it the key event, perhaps it can use it
if(menu_selected != key_selected)
{
// pass the keyevent
if(menu_selected != null_entity)
{
raise_key(menu_selected, keynr, ascii);
}
// if it is selectable the user perhaps wanted to reselect it
if(menu_selectable(menu_selected) == false || menu_selected == null_entity)
{
menu_selected = key_selected;
}
return;
}
// go on
}
// call current selected keydown function
// if nothing is selected -> window has no items -> call window key
if(menu_selected == null_entity)
{
// call window keydown
raise_key(menu_activewindow, keynr, ascii);
}
else
{
raise_key(menu_selected, keynr, ascii);
}
};
void(void) menu_selectprev =
{
entity temp;
temp = menu_selected;
// loop backward through the list until one item is selectable
while((temp = temp._prev) != null_entity)
if(menu_selectable(temp))
break;
if(temp != null_entity)
menu_selected = temp;
};
void(void) menu_selectnext =
{
entity temp;
temp = menu_selected;
// loop forward through the list until one item is selectable
while((temp = temp._next) != null_entity)
if(menu_selectable(temp))
break;
if(temp != null_entity)
menu_selected = temp;
};
void(void) menu_loopnext =
{
entity old;
old = menu_selected;
menu_selectnext();
if(menu_selected == old)
{
menu_selected = old._parent._child;
if(!menu_selectable(menu_selected))
menu_selectnext();
}
};
void(void) menu_loopprev =
{
entity old;
old = menu_selected;
menu_selectprev();
if(menu_selected == old)
{
while(old._next != null_entity)
{
old = old._next;
}
menu_selected = old;
if(!menu_selectable(old))
{
menu_selectprev();
}
}
}
void(void) menu_selectdown =
{
// move down a level, then search for a selectable child
// if none is found, then search for a sub-menu
// if one is found, recurse through it, else keep the old menu_selected
// (while recursing set the history)
entity ent, old_selected;
// if there is no child, return
if(menu_selected._child == null_entity)
{
return;
}
// loop through the children till a selectable is found
ent = menu_selected._child;
do
{
if(menu_selectable(ent))
{
// found one -> break
menu_selected = ent;
return;
}
} while((ent = ent._next) != null_entity);
// we found no selectable child, thus we loop through the children once again
// and recurse
ent = menu_selected._child;
old_selected = menu_selected;
do
{
if(ent._child != null_entity)
{
// add the history item
menu_pushhistory(ent._child);
// give it a try
menu_selected = ent;
menu_selectdown();
// found one ?
if(menu_selected != ent)
{
return;
}
// nothing found, then remove the history item
menu_pophistory();
}
} while((ent = ent._next) != null_entity);
// we didnt find anything
menu_selected = old_selected;
}
void(void) menu_selectup =
{
entity ent;
// move up till we find a selectable item
// if we reach the 'main' menu without having found such an item
// then toggle the menu (if possible) else select the first item available
// if we find the active_menu on our way up, reset it, so everything gets displayed
// correct
ent = menu_selected;
// reset menu_selected
menu_selected = null_entity;
print("Selectup called !\n");
while(1)
{
eprint(ent);
// does a history entry exist for the current object ?
if(menu_verifyhistory(ent))
{
menu_pophistory();
break;
}
// reset the active window if we reached it
if(ent == menu_activewindow) menu_activewindow = null_entity;
// move up
if(ent._parent != null_entity)
{
ent = ent._parent;
}
else
{
// nothing found -> break
break;
}
menu_selected = ent;
// is it selecteable ?
if(menu_selectable(ent))
{
// we found our item !
break;
}
// try selectnext
menu_selectnext();
if(menu_selected != ent)
{
// we found it !
break;
}
// try selectprev
menu_selectprev();
if(menu_selected != ent)
{
// we found it !
break;
}
}
// evalute our results:
if(menu_selected == null_entity)
{
// something must have been selected else -> error !
error("Couldnt select any item !\n");
// have we reached the 'main' menu ?
}
else if(menu_selected.name == MENU_NORMAL_NAME || menu_selected.name == MENU_INGAME_NAME)
{
menu_toggle();
// set the active window to the 'main' window
menu_activewindow = ent;
// if the menu isnt toggled then select an item now !
menu_selectdown();
return;
}
// does the active menu needs to be reset ?
if(menu_activewindow == null_entity)
{
if(menu_selected._parent)
menu_activewindow = ent._parent;
else menu_activewindow = ent;
}
};
void(void) menu_reselect =
{
menu_selected = menu_activewindow;
menu_selectdown();
};
void(entity menu, float setactive) menu_jumptowindow =
{
// only jump to windows
if(menu.type != "ITEM_WINDOW")
error("Cant jump to ", menu.name, " !\n");
// add a history point
menu_pushhistory(menu);
if(setactive)
menu_activewindow = menu;
// now set the selected to the first selectable child
menu_selected = menu;
menu_selectdown();
};
entity(string item_name) menu_getitem =
{
return findstring(null_entity, name, item_name);
};
void(entity ent) menu_removeitem =
{
// raise the destroy event
raise_destroy(ent);
remove(ent);
};
// history stuff
void(entity ent) menu_pushhistory =
{
entity his;
/*
// dont create multiple histories for the same 'trigger'
if(menu_history)
{
if(menu_history._next == ent)
return;
}*/
his = spawn();
his.type = "MMANAGER_HISTORY";
his._prev = menu_history;
his._child = menu_selected;
his._parent = menu_activewindow;
his._next = ent; // "used for"
menu_history = his;
};
void(void) menu_pophistory =
{
entity tmp;
if(menu_history == null_entity)
{
return;
}
menu_selected = menu_history._child;
menu_activewindow = menu_history._parent;
tmp = menu_history;
menu_history = menu_history._prev;
remove(tmp);
};
float(entity ent) menu_verifyhistory =
{
if(menu_history == null_entity)
return false;
if(menu_history._next == ent)
return true;
return false;
};
void(void) menu_clearhistory =
{
entity ent;
ent = null_entity;
while((ent = findstring(ent, type, "MMANAGER_HISTORY")) != null_entity)
{
remove(ent);
}
menu_history = null_entity;
};