]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/menu.qc
menu background: allow different aspect-fixing modes: letterbox, crop, width, height...
[divverent/nexuiz.git] / data / qcsrc / menu / menu.qc
1 ///////////////////////////////////////////////
2 // Menu Source File
3 ///////////////////////
4 // This file belongs to dpmod/darkplaces
5 // AK contains all menu functions (especially the required ones)
6 ///////////////////////////////////////////////
7
8 float mouseButtonsPressed;
9 vector menuMousePos;
10 float menuShiftState;
11 float menuPrevTime;
12 float menuAlpha;
13 float menuLogoAlpha;
14 float prevMenuAlpha;
15 float menuInitialized;
16 float menuNotTheFirstFrame;
17 float menuMouseMode;
18
19 void SUB_Null() { };
20
21 void() m_init =
22 {
23         cvar_set("_menu_alpha", "0");
24
25         dprint_load();
26         check_unacceptable_compiler_bugs();
27
28         // list all game dirs (TEST)
29         if(cvar("developer"))
30         {
31                 float i;
32                 string s;
33                 for(i = 0; ; ++i)
34                 {
35                         s = getgamedirinfo(i, GETGAMEDIRINFO_NAME);
36                         if not(s)
37                                 break;
38                         print(s, ": ", getgamedirinfo(i, GETGAMEDIRINFO_DESCRIPTION));
39                 }
40         }
41 }
42
43 float MENU_ASPECT = 1.2; // 1280x1024
44 float conwidth_s, conheight_s, realconwidth, realconheight, screenconwidth, screenconheight;
45 void draw_reset_cropped()
46 {
47         draw_reset(screenconwidth, screenconheight, 0.5 * (realconwidth - screenconwidth), 0.5 * (realconheight - screenconheight));
48 }
49 void draw_reset_full()
50 {
51         draw_reset(realconwidth, realconheight, 0, 0);
52 }
53 void UpdateConWidthHeight()
54 {
55         conwidth_s = conwidth;
56         conheight_s = conheight;
57         realconwidth = cvar("vid_conwidth");
58         realconheight = cvar("vid_conheight");
59         if(realconwidth / realconheight > MENU_ASPECT)
60         {
61                 // widescreen
62                 conwidth = realconheight * MENU_ASPECT;
63                 conheight = realconheight;
64         }
65         else
66         {
67                 // squarescreen
68                 conwidth = realconwidth;
69                 conheight = realconwidth / MENU_ASPECT;
70         }
71         screenconwidth = conwidth;
72         screenconheight = conheight;
73         if(conwidth < 800)
74         {
75                 conheight *= 800 / conwidth;
76                 conwidth = 800;
77         }
78         if(conheight < 600)
79         {
80                 conwidth *= 600 / conheight;
81                 conheight = 600;
82         }
83         if(main)
84         {
85                 if(conwidth_s != conwidth || conheight_s != conheight)
86                 {
87                         draw_reset_cropped();
88                         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
89                 }
90         }
91 }
92
93 void() m_init_delayed =
94 {
95         float fh, glob, n, i;
96         string s;
97
98         dprint_load();
99
100         menuInitialized = 0;
101         if(!preMenuInit())
102                 return;
103         menuInitialized = 1;
104         GameCommand_Init();
105
106         RegisterWeapons();
107
108         fh = -1;
109         if(cvar_string("menu_skin") != "")
110         {
111                 draw_currentSkin = strcat("gfx/menu/", cvar_string("menu_skin"));
112                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
113         }
114         if(fh < 0)
115         if(cvar_defstring("menu_skin") != "")
116         {
117                 draw_currentSkin = strcat("gfx/menu/", cvar_defstring("menu_skin"));
118                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
119         }
120         if(fh < 0)
121         {
122                 draw_currentSkin = "gfx/menu/default";
123                 fh = fopen(strcat(draw_currentSkin, "/skinvalues.txt"), FILE_READ);
124         }
125         draw_currentSkin = strzone(draw_currentSkin);
126         while((s = fgets(fh)))
127         {
128                 // these two are handled by skinlist.qc
129                 if(substring(s, 0, 6) == "title ")
130                         continue;
131                 if(substring(s, 0, 7) == "author ")
132                         continue;
133                 n = tokenize_console(s);
134                 if(n >= 2)
135                         Skin_ApplySetting(argv(0), substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
136         }
137         fclose(fh);
138
139         glob = search_begin(strcat(draw_currentSkin, "/*.tga"), TRUE, TRUE);
140         if(glob >= 0)
141         {
142                 n = search_getsize(glob);
143                 for(i = 0; i < n; ++i)
144                         precache_pic(search_getfilename(glob, i));
145                 search_end(glob);
146         }
147
148         draw_setMousePointer(SKINGFX_CURSOR, SKINSIZE_CURSOR, SKINOFFSET_CURSOR);
149
150         conwidth = conheight = -1;
151         UpdateConWidthHeight();
152         draw_reset_cropped();
153
154         loadTooltips();
155         main = spawnMainWindow(); main.configureMainWindow(main);
156         unloadTooltips();
157
158         main.resizeNotify(main, '0 0 0', eX * conwidth + eY * conheight, '0 0 0', eX * conwidth + eY * conheight);
159         main.focused = 1;
160         menuShiftState = 0;
161         menuMousePos = '0.5 0.5 0';
162
163         if(Menu_Active)
164                 m_display(); // delayed menu display
165 };
166
167 void(float key, float ascii) m_keyup =
168 {
169         if(!menuInitialized)
170                 return;
171         if(!Menu_Active)
172                 return;
173         draw_reset_cropped();
174         main.keyUp(main, key, ascii, menuShiftState);
175         if(key >= K_MOUSE1 && key <= K_MOUSE3)
176         {
177                 --mouseButtonsPressed;
178                 if(!mouseButtonsPressed)
179                         main.mouseRelease(main, menuMousePos);
180                 if(mouseButtonsPressed < 0)
181                 {
182                         mouseButtonsPressed = 0;
183                         print("Warning: released an already released button\n");
184                 }
185         }
186         if(key == K_ALT) menuShiftState -= (menuShiftState & S_ALT);
187         if(key == K_CTRL) menuShiftState -= (menuShiftState & S_CTRL);
188         if(key == K_SHIFT) menuShiftState -= (menuShiftState & S_SHIFT);
189 };
190
191 void(float key, float ascii) m_keydown =
192 {
193         if(!menuInitialized)
194                 return;
195         if(!Menu_Active)
196                 return;
197         if(keyGrabber)
198         {
199                 entity e;
200                 e = keyGrabber;
201                 keyGrabber = NULL;
202                 e.keyGrabbed(e, key, ascii);
203         }
204         else
205         {
206                 draw_reset_cropped();
207                 if(key >= K_MOUSE1 && key <= K_MOUSE3)
208                         if(!mouseButtonsPressed)
209                                 main.mousePress(main, menuMousePos);
210                 if(!main.keyDown(main, key, ascii, menuShiftState))
211                         if(key == K_ESCAPE)
212                                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED)) // don't back out to console only
213                                         m_hide(); // disable menu on unhandled ESC
214         }
215         if(key >= K_MOUSE1 && key <= K_MOUSE3)
216         {
217                 ++mouseButtonsPressed;
218                 if(mouseButtonsPressed > 10)
219                 {
220                         mouseButtonsPressed = 10;
221                         print("Warning: pressed an already pressed button\n");
222                 }
223         }
224         if(key == K_ALT) menuShiftState |= S_ALT;
225         if(key == K_CTRL) menuShiftState |= S_CTRL;
226         if(key == K_SHIFT) menuShiftState |= S_SHIFT;
227 };
228
229 void(string img, float a, string algn, float force1) drawBackground =
230 {
231         vector sz;
232         float width_is_larger;
233         vector isz_w;
234         vector isz_h;
235         vector tl, ce, br, isz;
236         vector v;
237         string s;
238         string mode;
239         float i, l;
240         string c;
241         sz = draw_PictureSize(img);
242         // keep aspect of image
243         width_is_larger = (sz_x * draw_scale_y >= sz_y * draw_scale_x);
244
245         isz_w = '1 0 0' + '0 1 0' * ((sz_y / sz_x) * (draw_scale_x / draw_scale_y)); 
246         isz_h = '0 1 0' + '1 0 0' * ((sz_x / sz_y) * (draw_scale_y / draw_scale_x)); 
247
248         v_z = 0;
249
250         // mode "c"
251         if(width_is_larger)
252                 isz = isz_h;
253         else
254                 isz = isz_w;
255
256         for(i = 0; i < strlen(algn); ++i)
257         {
258                 br = '1 1 0' - isz;
259                 tl = '0 0 0';
260                 ce = 0.5 * (tl + br);
261                 c = substring(algn, i, 1);
262                 switch(c)
263                 {
264                         case "c": // crop
265                                 if(width_is_larger)
266                                         isz = isz_h;
267                                 else
268                                         isz = isz_w;
269                                 goto nopic;
270                         case "l": // letterbox
271                                 if(width_is_larger)
272                                         isz = isz_w;
273                                 else
274                                         isz = isz_h;
275                                 goto nopic;
276                         case "h": // height
277                                 isz = isz_h;
278                                 goto nopic;
279                         case "w": // width
280                                 isz = isz_w;
281                                 goto nopic;
282                         case "s": // stretch
283                                 isz = '1 1 0';
284                                 goto nopic;
285                         case "1": case "4": case "7": v_x = tl_x; break;
286                         case "2": case "5": case "8": v_x = ce_x; break;
287                         case "3": case "6": case "9": v_x = br_x; break;
288                         default: v_x = tl_x + (br_x - tl_x) * random(); break;
289                 }
290                 switch(c)
291                 {
292                         case "7": case "8": case "9": v_y = tl_y; break;
293                         case "4": case "5": case "6": v_y = ce_y; break;
294                         case "1": case "2": case "3": v_y = br_y; break;
295                         default: v_y = tl_y + (br_y - tl_y) * random(); break;
296                 }
297                 if(l == 0)
298                         draw_Picture(v, img, isz, '1 1 1', a);
299                 else if(force1)
300                         // force all secondary layers to use alpha 1. Prevents ugly issues
301                         // with overlap. It's a flag because it cannot be used for the
302                         // ingame background
303                         draw_Picture(v, strcat(img, "_l", ftos(l+1)), isz, '1 1 1', 1);
304                 else
305                         draw_Picture(v, strcat(img, "_l", ftos(l+1)), isz, '1 1 1', a);
306                 ++l;
307 :nopic
308         }
309 }
310
311 vector menuTooltipAveragedMousePos;
312 entity menuTooltipItem;
313 vector menuTooltipOrigin;
314 vector menuTooltipSize;
315 float menuTooltipAlpha;
316 float menuTooltipState; // 0: no tooltip, 1: fading in, 2: displaying, 3: fading out
317 float m_testmousetooltipbox(vector pos)
318 {
319         if(pos_x >= menuTooltipOrigin_x && pos_x < menuTooltipOrigin_x + menuTooltipSize_x)
320         if(pos_y >= menuTooltipOrigin_y && pos_y < menuTooltipOrigin_y + menuTooltipSize_y)
321                 return FALSE;
322         return TRUE;
323 }
324 float m_testtooltipbox(vector tooltippos)
325 {
326         if(tooltippos_x < 0)
327                 return FALSE;
328         if(tooltippos_y < 0)
329                 return FALSE;
330         if(tooltippos_x + menuTooltipSize_x > 1)
331                 return FALSE;
332         if(tooltippos_y + menuTooltipSize_y > 1)
333                 return FALSE;
334         /*
335         menuTooltipOrigin_x = rint(tooltippos_x * cvar("vid_width")) / cvar("vid_width");
336         menuTooltipOrigin_y = rint(tooltippos_y * cvar("vid_height")) / cvar("vid_height");
337         menuTooltipOrigin_z = 0;
338         */
339         menuTooltipOrigin = tooltippos;
340         return TRUE;
341 }
342 float m_allocatetooltipbox(vector pos)
343 {
344         vector avoidplus, avoidminus;
345         vector v;
346
347         avoidplus_x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x) / conwidth;
348         avoidplus_y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y) / conheight;
349         avoidplus_z = 0;
350
351         avoidminus_x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x) / conwidth + menuTooltipSize_x;
352         avoidminus_y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y) / conheight + menuTooltipSize_y;
353         avoidminus_z = 0;
354
355         // bottom right
356         v = pos + avoidplus;
357         if(m_testtooltipbox(v))
358                 return TRUE;
359         
360         // bottom center
361         v_x = pos_x - menuTooltipSize_x * 0.5;
362         if(m_testtooltipbox(v))
363                 return TRUE;
364
365         // bottom left
366         v_x = pos_x - avoidminus_x;
367         if(m_testtooltipbox(v))
368                 return TRUE;
369
370         // top left
371         v_y = pos_y - avoidminus_y;
372         if(m_testtooltipbox(v))
373                 return TRUE;
374
375         // top center
376         v_x = pos_x - menuTooltipSize_x * 0.5;
377         if(m_testtooltipbox(v))
378                 return TRUE;
379         
380         // top right
381         v_x = pos_x + avoidplus_x;
382         if(m_testtooltipbox(v))
383                 return TRUE;
384         
385         return FALSE;
386 }
387 entity m_findtooltipitem(entity root, vector pos)
388 {
389         entity it;
390         entity best;
391
392         best = world;
393         it = root;
394
395         while(it.instanceOfContainer)
396         {
397                 while(it.instanceOfNexposee && it.focusedChild)
398                 {
399                         it = it.focusedChild;
400                         pos = globalToBox(pos, it.Container_origin, it.Container_size);
401                 }
402                 if(it.instanceOfNexposee)
403                 {
404                         it = it.itemFromPoint(it, pos);
405                         if(it.tooltip)
406                                 best = it;
407                         it = world;
408                 }
409                 else if(it.instanceOfModalController)
410                         it = it.focusedChild;
411                 else
412                         it = it.itemFromPoint(it, pos);
413                 if(!it)
414                         break;
415                 if(it.tooltip)
416                         best = it;
417                 pos = globalToBox(pos, it.Container_origin, it.Container_size);
418         }
419
420         return best;
421 }
422 void m_tooltip(vector pos)
423 {
424         float f, i, w;
425         entity it;
426         vector fontsize, p;
427         string s;
428
429         fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
430
431         f = bound(0, frametime * 2, 1);
432         menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f;
433         f = vlen(pos - menuTooltipAveragedMousePos);
434
435         if(f < 0.01)
436                 it = m_findtooltipitem(main, pos);
437         else    
438                 it = world;
439
440         // float menuTooltipState; // 0: static, 1: fading in, 2: fading out
441         if(it != menuTooltipItem)
442         {
443                 switch(menuTooltipState)
444                 {
445                         case 0:
446                                 if(menuTooltipItem)
447                                 {
448                                         // another item: fade out first
449                                         menuTooltipState = 2;
450                                 }
451                                 else
452                                 {
453                                         // new item: fade in
454                                         menuTooltipState = 1;
455                                         menuTooltipItem = it;
456
457                                         menuTooltipOrigin_x = -1; // unallocated
458                                         i = 0;
459                                         w =  0;
460                                         getWrappedLine_remaining = it.tooltip;
461                                         while(getWrappedLine_remaining)
462                                         {
463                                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
464                                                 ++i;
465                                                 f = draw_TextWidth(s, FALSE);
466                                                 if(f > w)
467                                                         w = f;
468                                         }
469                                         menuTooltipSize_x = w * fontsize_x + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
470                                         menuTooltipSize_y = i * fontsize_y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
471                                         menuTooltipSize_z = 0;
472                                 }
473                                 break;
474                         case 1:
475                                 // changing item while fading in: fade out first
476                                 menuTooltipState = 2;
477                                 break;
478                         case 2:
479                                 // changing item while fading out: can't
480                                 break;
481                 }
482         }
483         else if(menuTooltipState == 2) // re-fade in?
484                 menuTooltipState = 1;
485
486         if(menuTooltipItem)
487                 if(!m_testmousetooltipbox(pos))
488                         menuTooltipState = 2; // fade out if mouse touches it
489
490         switch(menuTooltipState)
491         {
492                 case 1:
493                         menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1);
494                         if(menuTooltipAlpha == 1)
495                                 menuTooltipState = 0;
496                         break;
497                 case 2:
498                         menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1);
499                         if(menuTooltipAlpha == 0)
500                         {
501                                 menuTooltipState = 0;
502                                 menuTooltipItem = world;
503                         }
504                         break;
505         }
506
507         if(menuTooltipItem)
508         {
509                 if(menuTooltipOrigin_x < 0) // unallocated?
510                         m_allocatetooltipbox(pos);
511
512                 if(menuTooltipOrigin_x >= 0)
513                 {
514                         // draw the tooltip!
515                         p = SKINBORDER_TOOLTIP;
516                         p_x *= 1 / conwidth;
517                         p_y *= 1 / conheight;
518                         draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
519                         p = menuTooltipOrigin;
520                         p_x += SKINMARGIN_TOOLTIP_x / conwidth;
521                         p_y += SKINMARGIN_TOOLTIP_y / conheight;
522                         getWrappedLine_remaining = menuTooltipItem.tooltip;
523                         while(getWrappedLine_remaining)
524                         {
525                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
526                                 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, FALSE);
527                                 p_y += fontsize_y;
528                         }
529                 }
530         }
531 }
532
533 void() m_draw =
534 {
535         float t;
536         float realFrametime;
537
538         menuMouseMode = cvar("menu_mouse_absolute");
539
540         if(main)
541                 UpdateConWidthHeight();
542
543         if(!menuInitialized)
544         {
545                 // TODO draw an info image about this situation
546                 m_init_delayed();
547                 return;
548         }
549         if(!menuNotTheFirstFrame)
550         {
551                 menuNotTheFirstFrame = 1;
552                 if(Menu_Active)
553                 if(!cvar("menu_video_played"))
554                 {
555                         localcmd("set menu_video_played 1; cd loop $menu_cdtrack; play sound/announcer/male/welcome.ogg\n");
556                         menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading
557                 }
558         }
559
560         t = gettime();
561         realFrametime = frametime = min(0.2, t - menuPrevTime);
562         menuPrevTime = t;
563         time += frametime;
564
565         t = cvar("menu_slowmo");
566         if(t)
567         {
568                 frametime *= t;
569                 realFrametime *= t;
570         }
571         else
572                 t = 1;
573
574         if(Menu_Active)
575         {
576                 if(getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED))
577                         setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU);
578                 else
579                         m_hide();
580         }
581
582         if(cvar("cl_capturevideo"))
583                 frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
584
585         dprint_load();
586         gamestatus = 0;
587         if(isserver())
588                 gamestatus = gamestatus | GAME_ISSERVER;
589         if(clientstate() == CS_CONNECTED)
590                 gamestatus = gamestatus | GAME_CONNECTED;
591         if(cvar("developer"))
592                 gamestatus = gamestatus | GAME_DEVELOPER;
593
594         prevMenuAlpha = menuAlpha;
595         if(Menu_Active)
596         {
597                 if(menuAlpha == 0 && menuLogoAlpha < 2)
598                 {
599                         menuLogoAlpha = menuLogoAlpha + frametime * 2;
600                 }
601                 else
602                 {
603                         menuAlpha = min(1, menuAlpha + frametime * 5);
604                         menuLogoAlpha = 2;
605                 }
606         }
607         else
608         {
609                 menuAlpha = max(0, menuAlpha - frametime * 5);
610                 menuLogoAlpha = 2;
611         }
612
613         draw_reset_cropped();
614
615         if(!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
616         {
617                 if(menuLogoAlpha > 0)
618                 {
619                         draw_reset_full();
620                         drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, TRUE);
621                         draw_reset_cropped();
622                         if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
623                         {
624                                 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
625                                 draw_drawMousePointer(menuMousePos);
626                                 draw_alpha = 1;
627                         }
628                 }
629         }
630         else if(SKINALPHA_BACKGROUND_INGAME)
631         {
632                 if(menuAlpha > 0)
633                 {
634                         draw_reset_full();
635                         drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, FALSE);
636                         draw_reset_cropped();
637                 }
638         }
639
640         if(menuAlpha != prevMenuAlpha)
641                 cvar_set("_menu_alpha", ftos(menuAlpha));
642
643         draw_reset_cropped();
644         preMenuDraw();
645         draw_reset_cropped();
646
647         if(menuAlpha <= 0)
648         {
649                 if(prevMenuAlpha > 0)
650                         main.initializeDialog(main, main.firstChild);
651                 draw_reset_cropped();
652                 postMenuDraw();
653                 return;
654         }
655
656         draw_alpha *= menuAlpha;
657
658         if(menuMouseMode)
659         {
660                 vector newMouse;
661                 newMouse = globalToBox(getmousepos(), draw_shift, draw_scale);
662                 if(newMouse != '0 0 0')
663                         if(newMouse != menuMousePos)
664                         {
665                                 menuMousePos = newMouse;
666                                 if(mouseButtonsPressed)
667                                         main.mouseDrag(main, menuMousePos);
668                                 else
669                                         main.mouseMove(main, menuMousePos);
670                         }
671         }
672         else
673         {
674                 if(frametime > 0)
675                 {
676                         vector dMouse;
677                         dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
678                         if(dMouse != '0 0 0')
679                         {
680                                 dMouse = globalToBoxSize(dMouse, draw_scale);
681                                 menuMousePos += dMouse * cvar("menu_mouse_speed");
682                                 menuMousePos_x = bound(0, menuMousePos_x, 1);
683                                 menuMousePos_y = bound(0, menuMousePos_y, 1);
684                                 if(mouseButtonsPressed)
685                                         main.mouseDrag(main, menuMousePos);
686                                 else
687                                         main.mouseMove(main, menuMousePos);
688                         }
689                 }
690         }
691         main.draw(main);
692
693         m_tooltip(menuMousePos);
694
695         draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
696
697         draw_drawMousePointer(menuMousePos);
698
699         draw_reset_cropped();
700         postMenuDraw();
701
702         frametime = 0;
703 };
704
705 void() m_display =
706 {
707         Menu_Active = true;
708         setkeydest(KEY_MENU);
709         setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU));
710
711         if(!menuInitialized)
712                 return;
713
714         if(mouseButtonsPressed)
715                 main.mouseRelease(main, menuMousePos);
716         mouseButtonsPressed = 0;
717
718         main.focusEnter(main);
719         main.showNotify(main);
720 };
721
722 void() m_hide =
723 {
724         Menu_Active = false;
725         setkeydest(KEY_GAME);
726         setmousetarget(MT_CLIENT);
727
728         if(!menuInitialized)
729                 return;
730
731         main.focusLeave(main);
732         main.hideNotify(main);
733 };
734
735 void() m_toggle =
736 {
737         if(Menu_Active)
738                 m_hide();
739         else
740                 m_display();
741 };
742
743 void() m_shutdown =
744 {
745         entity e;
746
747         m_hide();
748         for(e = NULL; (e = nextent(e)) != NULL; )
749         {
750                 if(e.destroy)
751                         e.destroy(e);
752         }
753 };
754
755 void m_focus_item_chain(entity outermost, entity innermost)
756 {
757         if(innermost.parent != outermost)
758                 m_focus_item_chain(outermost, innermost.parent);
759         innermost.parent.setFocus(innermost.parent, innermost);
760 }
761
762 void m_activate_window(entity wnd)
763 {
764         entity par;
765         par = wnd.parent;
766         if(par)
767                 m_activate_window(par);
768
769         if(par.instanceOfModalController)
770         {
771                 if(wnd.tabSelectingButton)
772                         // tabs
773                         TabButton_Click(wnd.tabSelectingButton, wnd);
774                 else
775                         // root
776                         par.initializeDialog(par, wnd);
777         }
778         else if(par.instanceOfNexposee)
779         {
780                 // nexposee (sorry for violating abstraction here)
781                 par.selectedChild = wnd;
782                 par.animationState = 1;
783                 setFocusContainer(par, NULL);
784         }
785         else if(par.instanceOfContainer)
786         {
787                 // other containers
788                 if(par.focused)
789                         par.setFocus(par, wnd);
790         }
791 }
792
793 void m_setpointerfocus(entity wnd)
794 {
795         if(wnd.instanceOfContainer)
796         {
797                 entity focus = wnd.preferredFocusedGrandChild(wnd);
798                 if(focus)
799                 {
800                         menuMousePos = focus.origin + 0.5 * focus.size;
801                         menuMousePos_x *= 1 / conwidth;
802                         menuMousePos_y *= 1 / conheight;
803                         if(wnd.focused) // why does this never happen?
804                                 m_focus_item_chain(wnd, focus);
805                 }
806         }
807 }
808
809 void(string itemname) m_goto =
810 {
811         entity e;
812         if(!menuInitialized)
813                 return;
814         if(itemname == "") // this can be called by GameCommand
815         {
816                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))
817                         m_hide();
818                 else
819                 {
820                         m_activate_window(main.mainNexposee);
821                         m_display();
822                 }
823         }
824         else
825         {
826                 e = findstring(NULL, name, itemname);
827                 if(e)
828                 {
829                         m_hide();
830                         m_activate_window(e);
831                         m_setpointerfocus(e);
832                         m_display();
833                 }
834         }
835 }
836
837 void() m_goto_skin_selector =
838 {
839         if(!menuInitialized)
840                 return;
841         // TODO add code to switch back to the skin selector (no idea how to do it now)
842         m_goto("skinselector");
843 }
844
845 void() m_goto_video_settings =
846 {
847         if(!menuInitialized)
848                 return;
849         // TODO add code to switch back to the skin selector (no idea how to do it now)
850         m_goto("videosettings");
851 }