]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/menu.qc
also new: SKINCOLOR_BACKGROUND
[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         float i, l;
238         string c;
239         sz = draw_PictureSize(img);
240         // keep aspect of image
241         width_is_larger = (sz_x * draw_scale_y >= sz_y * draw_scale_x);
242
243         isz_w = '1 0 0' + '0 1 0' * ((sz_y / sz_x) * (draw_scale_x / draw_scale_y)); 
244         isz_h = '0 1 0' + '1 0 0' * ((sz_x / sz_y) * (draw_scale_y / draw_scale_x)); 
245
246         v_z = 0;
247
248         // mode "c"
249         if(width_is_larger)
250                 isz = isz_h;
251         else
252                 isz = isz_w;
253
254         for(i = 0; i < strlen(algn); ++i)
255         {
256                 br = '1 1 0' - isz;
257                 tl = '0 0 0';
258                 ce = 0.5 * (tl + br);
259                 c = substring(algn, i, 1);
260                 switch(c)
261                 {
262                         case "c": // crop
263                                 if(width_is_larger)
264                                         isz = isz_h;
265                                 else
266                                         isz = isz_w;
267                                 goto nopic;
268                         case "l": // letterbox
269                                 if(width_is_larger)
270                                         isz = isz_w;
271                                 else
272                                         isz = isz_h;
273                                 goto nopic;
274                         case "h": // height
275                                 isz = isz_h;
276                                 goto nopic;
277                         case "w": // width
278                                 isz = isz_w;
279                                 goto nopic;
280                         case "s": // stretch
281                                 isz = '1 1 0';
282                                 goto nopic;
283                         case "1": case "4": case "7": v_x = tl_x; break;
284                         case "2": case "5": case "8": v_x = ce_x; break;
285                         case "3": case "6": case "9": v_x = br_x; break;
286                         default: v_x = tl_x + (br_x - tl_x) * random(); break;
287                 }
288                 switch(c)
289                 {
290                         case "7": case "8": case "9": v_y = tl_y; break;
291                         case "4": case "5": case "6": v_y = ce_y; break;
292                         case "1": case "2": case "3": v_y = br_y; break;
293                         default: v_y = tl_y + (br_y - tl_y) * random(); break;
294                 }
295                 if(l == 0)
296                         draw_Picture(v, img, isz, '1 1 1', a);
297                 else if(force1)
298                         // force all secondary layers to use alpha 1. Prevents ugly issues
299                         // with overlap. It's a flag because it cannot be used for the
300                         // ingame background
301                         draw_Picture(v, strcat(img, "_l", ftos(l+1)), isz, '1 1 1', 1);
302                 else
303                         draw_Picture(v, strcat(img, "_l", ftos(l+1)), isz, '1 1 1', a);
304                 ++l;
305 :nopic
306         }
307 }
308
309 vector menuTooltipAveragedMousePos;
310 entity menuTooltipItem;
311 vector menuTooltipOrigin;
312 vector menuTooltipSize;
313 float menuTooltipAlpha;
314 float menuTooltipState; // 0: no tooltip, 1: fading in, 2: displaying, 3: fading out
315 float m_testmousetooltipbox(vector pos)
316 {
317         if(pos_x >= menuTooltipOrigin_x && pos_x < menuTooltipOrigin_x + menuTooltipSize_x)
318         if(pos_y >= menuTooltipOrigin_y && pos_y < menuTooltipOrigin_y + menuTooltipSize_y)
319                 return FALSE;
320         return TRUE;
321 }
322 float m_testtooltipbox(vector tooltippos)
323 {
324         if(tooltippos_x < 0)
325                 return FALSE;
326         if(tooltippos_y < 0)
327                 return FALSE;
328         if(tooltippos_x + menuTooltipSize_x > 1)
329                 return FALSE;
330         if(tooltippos_y + menuTooltipSize_y > 1)
331                 return FALSE;
332         /*
333         menuTooltipOrigin_x = rint(tooltippos_x * cvar("vid_width")) / cvar("vid_width");
334         menuTooltipOrigin_y = rint(tooltippos_y * cvar("vid_height")) / cvar("vid_height");
335         menuTooltipOrigin_z = 0;
336         */
337         menuTooltipOrigin = tooltippos;
338         return TRUE;
339 }
340 float m_allocatetooltipbox(vector pos)
341 {
342         vector avoidplus, avoidminus;
343         vector v;
344
345         avoidplus_x = (SKINAVOID_TOOLTIP_x + SKINSIZE_CURSOR_x - SKINOFFSET_CURSOR_x) / conwidth;
346         avoidplus_y = (SKINAVOID_TOOLTIP_y + SKINSIZE_CURSOR_y - SKINOFFSET_CURSOR_y) / conheight;
347         avoidplus_z = 0;
348
349         avoidminus_x = (SKINAVOID_TOOLTIP_x + SKINOFFSET_CURSOR_x) / conwidth + menuTooltipSize_x;
350         avoidminus_y = (SKINAVOID_TOOLTIP_y + SKINOFFSET_CURSOR_y) / conheight + menuTooltipSize_y;
351         avoidminus_z = 0;
352
353         // bottom right
354         v = pos + avoidplus;
355         if(m_testtooltipbox(v))
356                 return TRUE;
357         
358         // bottom center
359         v_x = pos_x - menuTooltipSize_x * 0.5;
360         if(m_testtooltipbox(v))
361                 return TRUE;
362
363         // bottom left
364         v_x = pos_x - avoidminus_x;
365         if(m_testtooltipbox(v))
366                 return TRUE;
367
368         // top left
369         v_y = pos_y - avoidminus_y;
370         if(m_testtooltipbox(v))
371                 return TRUE;
372
373         // top center
374         v_x = pos_x - menuTooltipSize_x * 0.5;
375         if(m_testtooltipbox(v))
376                 return TRUE;
377         
378         // top right
379         v_x = pos_x + avoidplus_x;
380         if(m_testtooltipbox(v))
381                 return TRUE;
382         
383         return FALSE;
384 }
385 entity m_findtooltipitem(entity root, vector pos)
386 {
387         entity it;
388         entity best;
389
390         best = world;
391         it = root;
392
393         while(it.instanceOfContainer)
394         {
395                 while(it.instanceOfNexposee && it.focusedChild)
396                 {
397                         it = it.focusedChild;
398                         pos = globalToBox(pos, it.Container_origin, it.Container_size);
399                 }
400                 if(it.instanceOfNexposee)
401                 {
402                         it = it.itemFromPoint(it, pos);
403                         if(it.tooltip)
404                                 best = it;
405                         it = world;
406                 }
407                 else if(it.instanceOfModalController)
408                         it = it.focusedChild;
409                 else
410                         it = it.itemFromPoint(it, pos);
411                 if(!it)
412                         break;
413                 if(it.tooltip)
414                         best = it;
415                 pos = globalToBox(pos, it.Container_origin, it.Container_size);
416         }
417
418         return best;
419 }
420 void m_tooltip(vector pos)
421 {
422         float f, i, w;
423         entity it;
424         vector fontsize, p;
425         string s;
426
427         fontsize = '1 0 0' * (SKINFONTSIZE_TOOLTIP / conwidth) + '0 1 0' * (SKINFONTSIZE_TOOLTIP / conheight);
428
429         f = bound(0, frametime * 2, 1);
430         menuTooltipAveragedMousePos = menuTooltipAveragedMousePos * (1 - f) + pos * f;
431         f = vlen(pos - menuTooltipAveragedMousePos);
432
433         if(f < 0.01)
434                 it = m_findtooltipitem(main, pos);
435         else    
436                 it = world;
437
438         // float menuTooltipState; // 0: static, 1: fading in, 2: fading out
439         if(it != menuTooltipItem)
440         {
441                 switch(menuTooltipState)
442                 {
443                         case 0:
444                                 if(menuTooltipItem)
445                                 {
446                                         // another item: fade out first
447                                         menuTooltipState = 2;
448                                 }
449                                 else
450                                 {
451                                         // new item: fade in
452                                         menuTooltipState = 1;
453                                         menuTooltipItem = it;
454
455                                         menuTooltipOrigin_x = -1; // unallocated
456                                         i = 0;
457                                         w =  0;
458                                         getWrappedLine_remaining = it.tooltip;
459                                         while(getWrappedLine_remaining)
460                                         {
461                                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
462                                                 ++i;
463                                                 f = draw_TextWidth(s, FALSE);
464                                                 if(f > w)
465                                                         w = f;
466                                         }
467                                         menuTooltipSize_x = w * fontsize_x + 2 * (SKINMARGIN_TOOLTIP_x / conwidth);
468                                         menuTooltipSize_y = i * fontsize_y + 2 * (SKINMARGIN_TOOLTIP_y / conheight);
469                                         menuTooltipSize_z = 0;
470                                 }
471                                 break;
472                         case 1:
473                                 // changing item while fading in: fade out first
474                                 menuTooltipState = 2;
475                                 break;
476                         case 2:
477                                 // changing item while fading out: can't
478                                 break;
479                 }
480         }
481         else if(menuTooltipState == 2) // re-fade in?
482                 menuTooltipState = 1;
483
484         if(menuTooltipItem)
485                 if(!m_testmousetooltipbox(pos))
486                         menuTooltipState = 2; // fade out if mouse touches it
487
488         switch(menuTooltipState)
489         {
490                 case 1:
491                         menuTooltipAlpha = bound(0, menuTooltipAlpha + 5 * frametime, 1);
492                         if(menuTooltipAlpha == 1)
493                                 menuTooltipState = 0;
494                         break;
495                 case 2:
496                         menuTooltipAlpha = bound(0, menuTooltipAlpha - 2 * frametime, 1);
497                         if(menuTooltipAlpha == 0)
498                         {
499                                 menuTooltipState = 0;
500                                 menuTooltipItem = world;
501                         }
502                         break;
503         }
504
505         if(menuTooltipItem)
506         {
507                 if(menuTooltipOrigin_x < 0) // unallocated?
508                         m_allocatetooltipbox(pos);
509
510                 if(menuTooltipOrigin_x >= 0)
511                 {
512                         // draw the tooltip!
513                         p = SKINBORDER_TOOLTIP;
514                         p_x *= 1 / conwidth;
515                         p_y *= 1 / conheight;
516                         draw_BorderPicture(menuTooltipOrigin, SKINGFX_TOOLTIP, menuTooltipSize, '1 1 1', menuTooltipAlpha, p);
517                         p = menuTooltipOrigin;
518                         p_x += SKINMARGIN_TOOLTIP_x / conwidth;
519                         p_y += SKINMARGIN_TOOLTIP_y / conheight;
520                         getWrappedLine_remaining = menuTooltipItem.tooltip;
521                         while(getWrappedLine_remaining)
522                         {
523                                 s = getWrappedLine(SKINWIDTH_TOOLTIP / fontsize_x, draw_TextWidth_WithoutColors);
524                                 draw_Text(p, s, fontsize, '1 1 1', SKINALPHA_TOOLTIP * menuTooltipAlpha, FALSE);
525                                 p_y += fontsize_y;
526                         }
527                 }
528         }
529 }
530
531 void() m_draw =
532 {
533         float t;
534         float realFrametime;
535
536         menuMouseMode = cvar("menu_mouse_absolute");
537
538         if(main)
539                 UpdateConWidthHeight();
540
541         if(!menuInitialized)
542         {
543                 // TODO draw an info image about this situation
544                 m_init_delayed();
545                 return;
546         }
547         if(!menuNotTheFirstFrame)
548         {
549                 menuNotTheFirstFrame = 1;
550                 if(Menu_Active)
551                 if(!cvar("menu_video_played"))
552                 {
553                         localcmd("set menu_video_played 1; cd loop $menu_cdtrack; play sound/announcer/male/welcome.ogg\n");
554                         menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading
555                 }
556         }
557
558         t = gettime();
559         realFrametime = frametime = min(0.2, t - menuPrevTime);
560         menuPrevTime = t;
561         time += frametime;
562
563         t = cvar("menu_slowmo");
564         if(t)
565         {
566                 frametime *= t;
567                 realFrametime *= t;
568         }
569         else
570                 t = 1;
571
572         if(Menu_Active)
573         {
574                 if(getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED))
575                         setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU);
576                 else
577                         m_hide();
578         }
579
580         if(cvar("cl_capturevideo"))
581                 frametime = t / cvar("cl_capturevideo_fps"); // make capturevideo work smoothly
582
583         dprint_load();
584         gamestatus = 0;
585         if(isserver())
586                 gamestatus = gamestatus | GAME_ISSERVER;
587         if(clientstate() == CS_CONNECTED)
588                 gamestatus = gamestatus | GAME_CONNECTED;
589         if(cvar("developer"))
590                 gamestatus = gamestatus | GAME_DEVELOPER;
591
592         prevMenuAlpha = menuAlpha;
593         if(Menu_Active)
594         {
595                 if(menuAlpha == 0 && menuLogoAlpha < 2)
596                 {
597                         menuLogoAlpha = menuLogoAlpha + frametime * 2;
598                 }
599                 else
600                 {
601                         menuAlpha = min(1, menuAlpha + frametime * 5);
602                         menuLogoAlpha = 2;
603                 }
604         }
605         else
606         {
607                 menuAlpha = max(0, menuAlpha - frametime * 5);
608                 menuLogoAlpha = 2;
609         }
610
611         draw_reset_cropped();
612
613         if(!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
614         {
615                 if(menuLogoAlpha > 0)
616                 {
617                         draw_reset_full();
618                         draw_Fill('0 0 0', '1 1 0', SKINCOLOR_BACKGROUND, 1);
619                         drawBackground(SKINGFX_BACKGROUND, bound(0, menuLogoAlpha, 1), SKINALIGN_BACKGROUND, TRUE);
620                         draw_reset_cropped();
621                         if(menuAlpha <= 0 && SKINALPHA_CURSOR_INTRO > 0)
622                         {
623                                 draw_alpha = SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1);
624                                 draw_drawMousePointer(menuMousePos);
625                                 draw_alpha = 1;
626                         }
627                 }
628         }
629         else if(SKINALPHA_BACKGROUND_INGAME)
630         {
631                 if(menuAlpha > 0)
632                 {
633                         draw_reset_full();
634                         drawBackground(SKINGFX_BACKGROUND_INGAME, menuAlpha * SKINALPHA_BACKGROUND_INGAME, SKINALIGN_BACKGROUND_INGAME, FALSE);
635                         draw_reset_cropped();
636                 }
637         }
638
639         if(menuAlpha != prevMenuAlpha)
640                 cvar_set("_menu_alpha", ftos(menuAlpha));
641
642         draw_reset_cropped();
643         preMenuDraw();
644         draw_reset_cropped();
645
646         if(menuAlpha <= 0)
647         {
648                 if(prevMenuAlpha > 0)
649                         main.initializeDialog(main, main.firstChild);
650                 draw_reset_cropped();
651                 postMenuDraw();
652                 return;
653         }
654
655         draw_alpha *= menuAlpha;
656
657         if(menuMouseMode)
658         {
659                 vector newMouse;
660                 newMouse = globalToBox(getmousepos(), draw_shift, draw_scale);
661                 if(newMouse != '0 0 0')
662                         if(newMouse != menuMousePos)
663                         {
664                                 menuMousePos = newMouse;
665                                 if(mouseButtonsPressed)
666                                         main.mouseDrag(main, menuMousePos);
667                                 else
668                                         main.mouseMove(main, menuMousePos);
669                         }
670         }
671         else
672         {
673                 if(frametime > 0)
674                 {
675                         vector dMouse;
676                         dMouse = getmousepos() * (frametime / realFrametime); // for capturevideo
677                         if(dMouse != '0 0 0')
678                         {
679                                 dMouse = globalToBoxSize(dMouse, draw_scale);
680                                 menuMousePos += dMouse * cvar("menu_mouse_speed");
681                                 menuMousePos_x = bound(0, menuMousePos_x, 1);
682                                 menuMousePos_y = bound(0, menuMousePos_y, 1);
683                                 if(mouseButtonsPressed)
684                                         main.mouseDrag(main, menuMousePos);
685                                 else
686                                         main.mouseMove(main, menuMousePos);
687                         }
688                 }
689         }
690         main.draw(main);
691
692         m_tooltip(menuMousePos);
693
694         draw_alpha = max(draw_alpha, SKINALPHA_CURSOR_INTRO * bound(0, menuLogoAlpha, 1));
695
696         draw_drawMousePointer(menuMousePos);
697
698         draw_reset_cropped();
699         postMenuDraw();
700
701         frametime = 0;
702 };
703
704 void() m_display =
705 {
706         Menu_Active = true;
707         setkeydest(KEY_MENU);
708         setmousetarget((menuMouseMode ? MT_CLIENT : MT_MENU));
709
710         if(!menuInitialized)
711                 return;
712
713         if(mouseButtonsPressed)
714                 main.mouseRelease(main, menuMousePos);
715         mouseButtonsPressed = 0;
716
717         main.focusEnter(main);
718         main.showNotify(main);
719 };
720
721 void() m_hide =
722 {
723         Menu_Active = false;
724         setkeydest(KEY_GAME);
725         setmousetarget(MT_CLIENT);
726
727         if(!menuInitialized)
728                 return;
729
730         main.focusLeave(main);
731         main.hideNotify(main);
732 };
733
734 void() m_toggle =
735 {
736         if(Menu_Active)
737                 m_hide();
738         else
739                 m_display();
740 };
741
742 void() m_shutdown =
743 {
744         entity e;
745
746         m_hide();
747         for(e = NULL; (e = nextent(e)) != NULL; )
748         {
749                 if(e.destroy)
750                         e.destroy(e);
751         }
752 };
753
754 void m_focus_item_chain(entity outermost, entity innermost)
755 {
756         if(innermost.parent != outermost)
757                 m_focus_item_chain(outermost, innermost.parent);
758         innermost.parent.setFocus(innermost.parent, innermost);
759 }
760
761 void m_activate_window(entity wnd)
762 {
763         entity par;
764         par = wnd.parent;
765         if(par)
766                 m_activate_window(par);
767
768         if(par.instanceOfModalController)
769         {
770                 if(wnd.tabSelectingButton)
771                         // tabs
772                         TabButton_Click(wnd.tabSelectingButton, wnd);
773                 else
774                         // root
775                         par.initializeDialog(par, wnd);
776         }
777         else if(par.instanceOfNexposee)
778         {
779                 // nexposee (sorry for violating abstraction here)
780                 par.selectedChild = wnd;
781                 par.animationState = 1;
782                 setFocusContainer(par, NULL);
783         }
784         else if(par.instanceOfContainer)
785         {
786                 // other containers
787                 if(par.focused)
788                         par.setFocus(par, wnd);
789         }
790 }
791
792 void m_setpointerfocus(entity wnd)
793 {
794         if(wnd.instanceOfContainer)
795         {
796                 entity focus = wnd.preferredFocusedGrandChild(wnd);
797                 if(focus)
798                 {
799                         menuMousePos = focus.origin + 0.5 * focus.size;
800                         menuMousePos_x *= 1 / conwidth;
801                         menuMousePos_y *= 1 / conheight;
802                         if(wnd.focused) // why does this never happen?
803                                 m_focus_item_chain(wnd, focus);
804                 }
805         }
806 }
807
808 void(string itemname) m_goto =
809 {
810         entity e;
811         if(!menuInitialized)
812                 return;
813         if(itemname == "") // this can be called by GameCommand
814         {
815                 if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))
816                         m_hide();
817                 else
818                 {
819                         m_activate_window(main.mainNexposee);
820                         m_display();
821                 }
822         }
823         else
824         {
825                 e = findstring(NULL, name, itemname);
826                 if(e)
827                 {
828                         m_hide();
829                         m_activate_window(e);
830                         m_setpointerfocus(e);
831                         m_display();
832                 }
833         }
834 }
835
836 void() m_goto_skin_selector =
837 {
838         if(!menuInitialized)
839                 return;
840         // TODO add code to switch back to the skin selector (no idea how to do it now)
841         m_goto("skinselector");
842 }
843
844 void() m_goto_video_settings =
845 {
846         if(!menuInitialized)
847                 return;
848         // TODO add code to switch back to the skin selector (no idea how to do it now)
849         m_goto("videosettings");
850 }