]> icculus.org git repositories - divverent/nexuiz.git/blob - menu/mmanager.qc
grappling hook
[divverent/nexuiz.git] / menu / mmanager.qc
1 ///////////////////////////////////////////////
2 // Menu Manager Source File
3 ///////////////////////
4 // This file belongs to dpmod/darkplaces
5 // AK contains the manager code
6 ///////////////////////////////////////////////
7
8 void(void) menu_init =
9 {
10         //registercvar("menu_reloadlist","0");
11
12         menu_load();
13 };
14
15 void(void) menu_load =
16 {
17         // load the menu files
18         float count, i;
19
20         count = tokenize(MENU_FILENAME_LIST);
21
22         for(i = 0; i < count; i = i + 1)
23         {
24                 menu_loadmenu(argv(i));
25                 dprint(argv(i), " loaded !\n");
26         }
27
28         menu_linkwindows();
29 };
30
31 void(string file) menu_addfiletolist =
32 {
33         float count, i;
34
35         count = tokenize(MENU_FILENAME_LIST);
36
37         for(i = 0; i < count; i = i + 1)
38         {
39                 if(argv(i) == file)
40                 {
41                         return;
42                 }
43         }
44
45         MENU_FILENAME_LIST = strcat(MENU_FILENAME_LIST," ",file);
46 };
47
48 void(void) menu_restart =
49 {
50         // actually we empty the ent list and load everything one more time, thats it
51         entity ent;
52         float  selfused;
53         string oldself;
54         string oldactive;
55         string oldselected;
56
57         // we backup the active window name and the name of the selected item
58         oldactive = menu_activewindow.name;
59         oldselected = menu_selected.name;
60         // backup self's name
61         if(self != null_entity)
62         {
63                 oldself = self.name;
64                 selfused = true;
65         }
66         else
67         {
68                 selfused = false;
69         }
70
71         // first clear the history
72         menu_clearhistory();
73
74         // we remove all items
75         ent = null_entity;
76         while((ent = nextent(ent)) != null_entity)
77         {
78                 menu_removeitem(ent);
79         }
80
81         // FIXME: here's a "little" hack (so PRVM_ED_Alloc replaces the items instead of creating new ones)
82         time = time + 1.0;
83
84         // now call menu_load
85         menu_load();
86
87         time = time - 1.0;      // we cant use gettime cause that always returns time :-/
88
89         // perform the init
90         menu_performreinit();
91
92         ent = menu_getitem(oldactive);
93         if(ent)
94         {
95                 menu_activewindow = ent;
96                 ent = menu_getitem(oldselected);
97                 if(ent)
98                 {
99                         menu_selected = ent;
100                 }
101         }
102
103         if(selfused)
104         {
105                 ent = menu_getitem(oldself);
106                 if(ent)
107                 {
108                         self = ent;
109                 } else // we have no current self...
110                 {
111                         error("Reloaded menu files, but the former self (", oldself ,") item is missing !\n");
112                 }
113         }
114 };
115 void(string file) menu_loadmenu =
116 {
117         loadfromfile(file);
118 };
119
120 entity(entity start, .entity find1, entity match, .float find2, float match2) findef =
121 {
122         while(1)
123         {
124                 start = findentity(start, find1, match);
125                 if(start == null_entity)
126                         break;
127                 if(start.find2 == match2)
128                         break;
129         }
130
131         return start;
132         /*while((start = findentity(start,find1,match))!=null_entity)
133                 if(start.find2 == match2)
134                         break;
135         return start;*/
136 };
137
138 void(void) menu_linkwindows =
139 {
140         // first verify that MENU_NORMAL_NAME and MENU_INGAME_NAME exist
141         // if not add the default strings
142         entity ent;
143         float x, opos;
144
145         dprint("Loading defaults if necessary\n");
146
147         ent = findstring(null_entity,name, MENU_NORMAL_NAME);
148         if(ent == null_entity)
149                 loadfromdata(MENU_NORMAL_DEFAULT);
150
151         // verify again if MENI_INGAME_NAME is there now
152         ent = findstring(null_entity,name, MENU_NORMAL_NAME);
153         if(ent == null_entity)
154                 error("Bad MENU_NORMAL_DEFAULT !\n");
155
156         ent = findstring(null_entity,name, MENU_INGAME_NAME);
157         if(ent == null_entity)
158                 loadfromdata(MENU_INGAME_DEFAULT);
159
160         // verify again if MENI_INGAME_NAME is there now
161         ent = findstring(null_entity,name, MENU_INGAME_NAME);
162         if(ent == null_entity)
163                 error("Bad MENU_INGAME_DEFAULT !\n");
164
165         dprint("Verifying that each name is used only once\n");
166
167         // verify that every name is only used *once*
168         ent = null_entity;
169         while((ent = nextent(ent)) != null_entity)
170         {
171                 self = ent;
172                 while((self = findstring(self, name, ent.name)) != null_entity)
173                 {
174                         if(self != null_entity)
175                         {
176                                 objerror("Name ", ent.name, " already used !\n");
177                         }
178                 }
179         }
180
181         dprint("Verification/Setting of: type, parent, next, prev and child fields\n");
182
183         // now we have to :
184         // set the parent field with parent_name
185         // set the next and prev fields
186         // set the child field
187         // check the type field
188         self = null_entity;
189         while((self = nextent(self)) != null_entity)
190         {
191                 if(self.name == "")
192                 {
193                         objerror("Name is missing !\n");
194                         continue;
195                 }
196
197                 if(self.type == "")
198                 {
199                         objerror("Type is missing !\n");
200                         continue;
201                 }
202
203                 if(!isfunction(self.type))
204                 {
205                         objerror("Control ", self.type, " not found !\n");
206                         continue;
207                 }
208
209                 // find parent
210                 // if parent_name is "" do nothing else set parent
211                 if(self.parent != "")
212                 {
213                         ent = findstring(null_entity, name, self.parent);
214
215                         if(ent == null_entity)
216                         {
217                                 objerror("Item ", self.parent, " not found !\n");
218                                 continue;
219                         }
220
221                         self._parent = ent;
222                 }
223                 else
224                 {
225                         self._parent = null_entity;
226                 }
227         }
228
229         dprint("Calling the type functions\n");
230
231         // call the type functions (former classname functions)
232         ent = null_entity;
233         while((ent = nextent(ent)) != null_entity)
234         {
235                 self = ent;
236                 //dprint("Calling ",self.type," (", etos(self),")\n");
237                 callfunction(self.type);
238         }
239
240         dprint("Orderpos is verified or set\n");
241
242         // now auto-set all ents with orderpos 0
243         self = null_entity;
244         while((self = findfloat(self,orderpos, 0)) != null_entity)
245         {
246                 if(self.parent == "")
247                         continue;
248
249                 // now go through all orderpos' beginning from 1
250                 opos = 1;
251                 while((ent = findef(null_entity, _parent, self._parent, orderpos, opos)) != null_entity)
252                 {
253                         opos = opos + 1;
254                 }
255
256                 self.orderpos = opos;
257         }
258
259         dprint("Set the _child, _prev and _next fields\n");
260
261         self = null_entity;
262         while((self = nextent(self)) != null_entity)
263         {
264                 // find first child
265                 // orderpos starts with 1
266                 ent = findef(null_entity, _parent, self, orderpos, 1);
267
268                 if(ent == null_entity)
269                 {
270                         if(findentity(ent, _parent, self) != null_entity)
271                         {
272                                 objerror("Order pos 1 is missing in the child list of ", self.name, " !\n");
273                                 continue;
274                         }
275                         //else doesnt have any chilren
276                 }
277                 else
278                         self._child = ent;
279
280                 // add to next, previous list
281                 // find orderpos - x (starting with x = 1)
282                 x = self.orderpos;
283
284                 while(x > 1)
285                 {
286                         x = x - 1;
287
288                         ent = findef(null_entity, _parent, self._parent, orderpos, x);
289                         if(ent != null_entity)
290                         {
291                                 self._prev = ent;
292                                 ent._next = self;
293                                 break;
294                         }
295                 }
296
297                 // find orderpos + x (starting with x = 1 until x == self.oderpos + 100)
298                 x = self.orderpos;
299
300                 while(x < self.orderpos + 100)
301                 {
302                         x = x + 1;
303
304                         ent = findef(null_entity, _parent, self._parent, orderpos, x);
305                         if(ent != null_entity)
306                         {
307                                 self._next = ent;
308                                 ent._prev = self;
309                                 break;
310                         }
311                 }
312         }
313
314         dprint("Linking windows finished.\n");
315 };
316
317 void(void) menu_toggle =
318 {
319         // only let the qc toggle the menu if we are ingame or a developer
320         if(gamestatus & GAME_CONNECTED || cvar("developer"))
321         {
322                 // then allow toggling
323                 m_toggle();
324         }// else do nothing
325 };
326
327 void(void) menu_performreinit =
328 {
329         // clear history
330         menu_clearhistory();
331
332         // reset the key hook (if necessary at all)
333         menu_keyhook = null_function;
334
335         // and reinit all menu items
336         self = null_entity;
337         while((self = nextent(self)) != null_entity)
338         {
339                 if(self.parent == "")
340                         self._parent = null_entity;
341                 //else actually this shouldnt happen
342                 else if(self._parent.name != self.parent)
343                         objerror("Parent (should be ", self.parent, ") of non-menu item ", self.name, " changed to ", self._parent.name, " !\n");
344
345                 raise_reinit(self); // always call reinit
346         }
347
348         // choose which menu to display
349         if(MENU_ALLOWINGAME && (gamestatus & GAME_CONNECTED))
350                 menu_activewindow = findstring(null_entity, name, MENU_INGAME_NAME);
351         else
352                 menu_activewindow = findstring(null_entity, name, MENU_NORMAL_NAME);
353
354         // set the selected item
355         menu_selected = menu_activewindow;
356
357         // find first child that can be selected
358         menu_selectdown();
359 };
360
361 void(entity par, float selectstate) menu_processmouse =
362 {
363         // self is parent
364         // loop through all childs
365         // and try to find an object whose click rect fits to the mouse coords
366         entity ent;
367         vector old_cursor, local_cursor;
368         vector old_c_size, old_c_pos, clipped_size;
369
370         ent = par._child;
371         if(ent == null_entity)
372                 return;
373
374         if(menu_clip_size != '0 0 0')
375         {
376                 clipped_size = cliprectsize(gfx_conmentogfx(par.clip_pos),par.clip_size, menu_clip_pos, menu_clip_size);
377         }
378         else
379         {
380                 clipped_size = par.clip_size;
381         }
382
383         if(clipped_size != '0 0 0')
384         {
385                 // do clip the clip area
386                 // save the old
387                 old_c_pos = menu_clip_pos;
388                 old_c_size = menu_clip_size;
389
390                 // clip the position
391                 menu_clip_pos = cliprectpos(gfx_conmentogfx(par.clip_pos), par.clip_size, menu_clip_pos, menu_clip_size);
392                 menu_clip_size = clipped_size;
393         }
394
395         menu_localorigin = menu_localorigin + par.origin;
396
397         old_cursor = menu_cursor;
398         local_cursor = gfx_congfxtomen(cursor);
399
400         //print(vtos(cursor)," ");print(vtos(menu_clip_pos)," ");print(vtos(menu_clip_size),"\n");
401
402         if(inrect(cursor, menu_clip_pos, menu_clip_size) || menu_clip_size == '0 0 0')
403         {
404                 do
405                 {
406                         // if not visible, continue to the next item
407                         if(menu_isvisible(ent))
408                         {
409                                 old_cursor = menu_cursor;
410                                 menu_cursor = local_cursor;
411                                 if(inrect(local_cursor, ent.click_pos, ent.click_size))
412                                 {
413                                         // call mouse_enter event ?
414                                         if(!(ent.flag & _FLAG_MOUSEINAREA) && menu_hasevents(ent))
415                                         {
416                                                 raise_mouse_enter(ent);
417
418                                                 ent.flag = ent.flag | _FLAG_MOUSEINAREA;
419                                         }
420
421                                         // select it ?
422                                         if(menu_selectable(ent) && selectstate != MENU_SELECT_NEVER)
423                                         {
424                                                 menu_selected = ent;
425                                         }
426                                         else if(selectstate == MENU_SELECT_ALWAYS)
427                                         {
428                                                 if(menu_hasevents(ent))
429                                                 {
430                                                         menu_selected = ent;
431                                                 }
432                                         }
433                                 }
434                                 else
435                                 {
436                                         // call mouse_leave event ?
437                                         if((ent.flag & _FLAG_MOUSEINAREA) && menu_hasevents(ent))
438                                         {
439                                                 raise_mouse_leave(ent);
440
441                                                 // this only works if _FLAG_MOUSEINAREA is set
442                                                 ent.flag = ent.flag - _FLAG_MOUSEINAREA;
443                                         }
444                                 }
445
446                                 if(menu_selected != ent)
447                                         menu_cursor = old_cursor;
448
449
450                                 // if ent has children recurse through them
451                                 if(ent._child != null_entity)
452                                 {
453                                         if((ent.flag & FLAG_CHILDDRAWONLY) || (ent.flag & FLAG_CHILDDRAWREFRESHONLY))
454                                         {
455                                                 menu_processmouse(ent, MENU_SELECT_NEVER);
456                                         }
457                                         else
458                                         {
459                                                 menu_processmouse(ent, selectstate);
460                                         }
461                                 }
462                         }
463                 } while((ent = ent._next) != null_entity);
464         }
465
466         menu_localorigin = menu_localorigin - par.origin;
467
468         // restore the old menu_clip vars if necessary
469         if(clipped_size != '0 0 0')
470         {
471                 menu_clip_size = old_c_size;
472                 menu_clip_pos = old_c_pos;
473         }
474 };
475
476 void(void) menu_frame =
477 {
478         /*
479         // this is only for debugging purposes
480         // thus its unstable and *won't* work any more when Ive changed dp's behavior with
481         // the builtin list (the precache functions will only work for menu_init)
482         if(cvar("menu_reloadlist"))
483         {
484                 cvar_set("menu_reloadlist","0");
485                 menu_restart();
486         }*/
487         menu_automatedselection = false;
488         // if mouse moved, process it
489         if(cursor_rel != '0 0 0')
490         {
491                 menu_processmouse(menu_activewindow, MENU_SELECT_SELECTABLE);
492         }
493
494 }
495
496 void(entity menu) menu_drawwindow =
497 {
498         // loop through all children and draw them
499         entity ent;
500         vector old_c_pos, old_c_size, clipped_size, clipped_pos;
501
502         // set the clipping area
503         // is this necessary at all ?
504         if(menu_clip_size != '0 0 0')
505         {
506                 clipped_size = cliprectsize(gfx_conmentogfx(menu.clip_pos),menu.clip_size, menu_clip_pos, menu_clip_size);
507         }
508         else
509         {
510                 clipped_size = menu.clip_size;
511         }
512
513         if(clipped_size != '0 0 0')
514         {
515                 // do clip the clip area
516                 // save the old
517                 old_c_pos = menu_clip_pos;
518                 old_c_size = menu_clip_size;
519
520                 // clip the position
521                 menu_clip_pos = cliprectpos( gfx_conmentogfx(menu.clip_pos) , menu.clip_size, menu_clip_pos, menu_clip_size);
522                 menu_clip_size = clipped_size;
523                 gfx_setcliparea(menu_clip_pos, menu_clip_size);
524         }
525
526         // set the localorigin (the clipping position wont be affected)
527         menu_localorigin = menu_localorigin + menu.origin;
528
529         ent = menu._child;
530         do
531         {
532                 // if it's not visible continue
533                 if(menu_isvisible(ent))
534                 {
535                         self = ent;
536                         if(menu_hasevents(ent))
537                         {
538                                 raise_refresh(ent);
539                         } else if(ent.flag & FLAG_DRAWREFRESHONLY)
540                         {
541                                 raise_refresh(ent);
542                         } else if(ent.flag & FLAG_DRAWONLY)
543                         {
544                                 // TODO: find a better solution for this hack
545                         } else if(ent._parent)
546                         {
547                                 if(ent._parent.flag & FLAG_CHILDDRAWREFRESHONLY)
548                                         raise_refresh(ent);
549                         }
550
551                         raise_draw(ent);
552
553                         if(ent._child != null_entity)
554                         {
555                                 menu_drawwindow(ent);
556                         }
557
558                 }
559         } while((ent = ent._next) != null_entity);
560
561         menu_localorigin = menu_localorigin - menu.origin;
562
563         // restore the old menu_clip vars if necessary
564         if(clipped_size != '0 0 0')
565         {
566                 menu_clip_size = old_c_size;
567                 menu_clip_pos = old_c_pos;
568         }
569
570         // reset the clip area of the old menu
571         if(menu_clip_size != '0 0 0')
572                 gfx_setcliparea(menu_clip_pos, menu_clip_size);
573         else
574                 gfx_resetcliparea();
575 }
576
577 void(void) menu_draw =
578 {
579         // if menu_activewindow is visible loop though it
580         if(menu_isvisible(menu_activewindow))
581         {
582                 //menu_setcliparea('100 100 0', '400 400 0');
583                 menu_drawwindow(menu_activewindow);
584                 menu_localorigin = '0 0 0';
585                 menu_clip_pos = '0 0 0';
586                 menu_clip_size = '0 0 0';
587                 menu_resetcliparea();
588         }
589 }
590
591 float(entity e) menu_hasevents =
592 {
593         if(e.flag & FLAG_DRAWONLY)
594                 return false;
595         if(e.flag & FLAG_DRAWREFRESHONLY)
596                 return false;
597         if(e._parent)
598         {
599                 if(e._parent.flag & FLAG_CHILDDRAWONLY)
600                         return false;
601                 if(e._parent.flag & FLAG_CHILDDRAWREFRESHONLY)
602                         return false;
603         }
604         if(menu_isvisible(e))
605                 return true;
606         return false;
607 };
608
609 float(entity e) menu_isvisible =
610 {
611         if(e.flag & FLAG_HIDDEN)
612                 return false;
613
614         if((e.flag & FLAG_SERVERONLY) && !(gamestatus & GAME_ISSERVER))
615                 return false;
616
617         if((e.flag & FLAG_DEVELOPERONLY) && !(gamestatus & GAME_DEVELOPER))
618                 return false;
619
620         return true;
621 };
622
623 float(entity e) menu_selectable =
624 {
625         if(!menu_hasevents(e))
626                 return false;
627         if(e.flag & FLAG_NOSELECT)
628                 return false;
629
630         //if(e == menu_getitem("quit"))
631         //      crash();
632
633         return true;
634 };
635
636 void(void) menu_shutdown =
637 {
638         // call the terminate event for each object
639         self = null_entity;
640         while((self = nextent(self)) != null_entity)
641         {
642                 raise_destroy(self);
643         }
644 };
645
646 void(float keynr, float ascii) menu_keydown =
647 {
648         // is a keyhook set ?
649         if(menu_keyhook != null_function)
650         {
651                 // call it
652                 menu_keyhook(keynr, ascii);
653                 return;
654         }
655         // before calling the current keydown functions, process the mouse again
656         // so only the correct item is called
657         // (except mouse wheel up and down)
658         // if the mouse doesnt point to an item, there wont be a reaction on the clicking
659         if(K_MOUSE1 <= keynr && keynr <= K_MOUSE10)
660         {
661                 entity key_selected;
662                 key_selected = menu_selected;
663                 menu_selected = null_entity;
664                 menu_processmouse(menu_activewindow, MENU_SELECT_ALWAYS);
665
666                 // if we find anything, we give it the key event, perhaps it can use it
667                 if(menu_selected != key_selected)
668                 {
669                         // pass the keyevent
670                         if(menu_selected != null_entity)
671                         {
672                                 raise_key(menu_selected, keynr, ascii);
673                         }
674
675                         // if it is selectable the user perhaps wanted to reselect it
676                         if(menu_selectable(menu_selected) == false || menu_selected == null_entity)
677                         {
678                                 menu_selected = key_selected;
679                         }
680
681                         return;
682                 }
683                 // go on
684         }
685
686         // call current selected keydown function
687         // if nothing is selected -> window has no items -> call window key
688         if(menu_selected == null_entity)
689         {
690                 // call window keydown
691                 raise_key(menu_activewindow, keynr, ascii);
692         }
693         else if(menu_hasevents(menu_selected))
694         {
695                 raise_key(menu_selected, keynr, ascii);
696         }
697 };
698
699 void(void) menu_selectprev =
700 {
701         entity temp;
702
703         temp = menu_selected;
704         // loop backward through the list until one item is selectable
705         while((temp = temp._prev) != null_entity)
706                 if(menu_selectable(temp))
707                         break;
708
709         if(temp != null_entity)
710                 menu_selected = temp;
711 };
712
713 void(void) menu_selectnext =
714 {
715         entity temp;
716
717         temp = menu_selected;
718         // loop forward through the list until one item is selectable
719         while((temp = temp._next) != null_entity)
720                 if(menu_selectable(temp))
721                         break;
722
723         if(temp != null_entity)
724                 menu_selected = temp;
725 };
726
727 void(void) menu_loopnext =
728 {
729         entity old;
730         old = menu_selected;
731
732         menu_selectnext();
733         if(menu_selected == old)
734         {
735                 menu_selected = old._parent._child;
736                 if(!menu_selectable(menu_selected))
737                         menu_selectnext();
738         }
739 };
740
741 void(void) menu_loopprev =
742 {
743         entity old;
744         old = menu_selected;
745         menu_selectprev();
746         if(menu_selected == old)
747         {
748                 while(old._next != null_entity)
749                 {
750                         old = old._next;
751                 }
752
753                 menu_selected = old;
754                 if(!menu_selectable(old))
755                 {
756                         menu_selectprev();
757                 }
758         }
759 }
760
761 void(void) menu_selectdown =
762 {
763         // move down a level, then search for a selectable child
764         // if none is found, then search for a sub-menu
765         // if one is found, recurse through it, else keep the old menu_selected
766         // (while recursing set the history)
767
768         entity ent, old_selected;
769
770         // if there is no child, return
771         if(menu_selected._child == null_entity)
772         {
773                 return;
774         }
775
776         // loop through the children till a selectable is found
777         ent = menu_selected._child;
778         do
779         {
780                 if(menu_selectable(ent))
781                 {
782                         // found one -> break
783                         menu_selected = ent;
784                         return;
785                 }
786         } while((ent = ent._next) != null_entity);
787
788         // we found no selectable child, thus we loop through the children once again
789         // and recurse
790         ent = menu_selected._child;
791         old_selected = menu_selected;
792         do
793         {
794                 if(ent._child != null_entity)
795                 {
796                         if(!(ent.flag & FLAG_CHILDDRAWONLY) && !(ent.flag & FLAG_CHILDDRAWREFRESHONLY))
797                         {
798                                 // give it a try
799                                 menu_selected = ent;
800                                 menu_selectdown();
801
802                                 // found one ?
803                                 if(menu_selected != ent)
804                                 {
805                                         return;
806                                 }
807                         }
808
809
810                 }
811         } while((ent = ent._next) != null_entity);
812
813         // we didnt find anything
814         menu_selected = old_selected;
815 }
816
817
818 void(void) menu_selectup =
819 {
820         // if we try to up from the current activewindow pop the history.
821         // else move up in the hierarchy and try to select and item.
822
823         //print( "Check whether menu_selected == menu_activewindow\n" );
824         if( menu_selected == menu_activewindow ) {
825                 //print( "Pop history\n" );
826                 if( menu_history == null_entity ) {
827                         //print( "History empty!\n" );
828                         menu_toggle();
829                         menu_reselect();
830                 }
831                 menu_pophistory();
832                 return;
833         }
834
835         //print( "Check whether parent exists\n" );
836         if( menu_selected._parent == null_entity )
837                 return;
838
839         //print( "Try to select the parent\n" );
840         menu_selected = menu_selected._parent;
841         if( menu_selectable( menu_selected ) )
842                 return;
843
844         //print( "Try to select an item after the parent\n" );
845         menu_selectnext();
846         if( menu_selectable( menu_selected ) )
847                 return;
848
849         //print( "Try to select an item before the parent\n" );
850         menu_selectprev();
851         if( menu_selectable( menu_selected ) )
852                 return;
853
854         //print( "Try to select the parent of the parent\n" );
855         menu_selectup();
856
857         //print( "Selected:\n" );
858         //eprint( menu_selected );
859 };
860
861 void(void) menu_reselect =
862 {
863         menu_selected = menu_activewindow;
864         menu_selectdown();
865 };
866
867 void(entity menu, float setactive) menu_jumptowindow =
868 {
869         // only jump to windows
870         if(menu.type != "ITEM_WINDOW" && menu.type != "ITEM_REFERENCE")
871                 error("Cant jump to ", menu.name, " !\n");
872
873         // add a history point
874         if(setactive) {
875                 menu_pushhistory(menu);
876                 menu_activewindow = menu;
877         }
878
879         // now set the selected to the first selectable child
880         menu_selected = menu;
881         menu_automatedselection = true;
882         menu_selectdown();
883 };
884
885 entity(string item_name) menu_getitem =
886 {
887         entity item;
888
889         item = findstring(null_entity,  name, item_name);
890         if( item == null_entity )
891                 error( "Couldn't find item '", item_name, "'!" );
892
893         return item;
894 };
895
896 void(entity ent) menu_removeitem =
897 {
898         // raise the destroy event
899         raise_destroy(ent);
900         remove(ent);
901 };
902
903
904 // history stuff
905
906 void(entity ent) menu_pushhistory =
907 {
908         entity his;
909
910         /*
911         // dont create multiple histories for the same 'trigger'
912         if(menu_history)
913         {
914                 if(menu_history._next == ent)
915                         return;
916         }*/
917
918         menu_keyhook = null_function;
919
920         his = spawn();
921
922         his.type = "MMANAGER_HISTORY";
923         his._prev = menu_history;
924         his._child = menu_selected;
925         his._parent = menu_activewindow;
926         his._next = ent; // "used for"
927
928         menu_history = his;
929 };
930
931 void(void) menu_pophistory =
932 {
933         entity tmp;
934
935         if(menu_history == null_entity)
936         {
937                 return;
938         }
939
940         menu_keyhook = null_function;
941
942         menu_selected = menu_history._child;
943         menu_activewindow = menu_history._parent;
944
945         tmp = menu_history;
946         menu_history = menu_history._prev;
947
948         remove(tmp);
949 };
950
951 float(entity ent) menu_verifyhistory =
952 {
953         if(menu_history == null_entity)
954                 return false;
955
956         if(menu_history._next == ent)
957                 return true;
958         return false;
959 };
960
961 void(void) menu_clearhistory =
962 {
963         entity ent;
964
965         ent = null_entity;
966         while((ent = findstring(ent, type, "MMANAGER_HISTORY")) != null_entity)
967         {
968                 remove(ent);
969         }
970
971         menu_history = null_entity;
972 };