1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2013 COR Entertainment, LLC.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "client.h"
30 #include "qmenu.h"
31
32 static void ItemName_Draw (menuaction_s *a, FNT_font_t font, const float *color);
33 static void Action_Draw (menuaction_s *a, FNT_font_t font);
34 static void Menu_DrawStatusBar( const char *string );
35 static void Menu_DrawToolTip (const menuitem_s *item);
36 static void Label_Draw (menutxt_s *s, FNT_font_t font, const float *color);
37 static void Slider_DoSlide( menuslider_s *s, int dir );
38 static void Slider_Draw (menuslider_s *s, FNT_font_t font);
39 static void SpinControl_Draw (menulist_s *s, FNT_font_t font);
40 static void SpinControl_DoSlide( menulist_s *s, int dir );
41 static void SubMenu_Draw (menuframework_s *s, FNT_font_t font);
42
43 static void Menu_DrawHorizBar (const char *pathbase, float x, float y, float w, float base_size, float alpha);
44 static void Menu_DrawVertBar (const char *pathbase, float x, float y, float h, float base_size, float alpha);
45
46 void Menu_DrawBorder (menuframework_s *menu, const char *title, const char *prefix);
47
48 static menuvec2_t Menu_GetBorderSize (menuframework_s *s);
49
50 // dark color = same blue as icons, light color = white
51 const float dark_color[4] = {0, 1, 200.0f/255.0f, 1};
52 const float light_color[4] = {1, 1, 1, 1};
53 const float highlight_color[4] = {1, 1, 1, 0.95};
54
55 #define VID_WIDTH viddef.width
56 #define VID_HEIGHT viddef.height
57
Menu_PredictSize(const char * str)58 int Menu_PredictSize (const char *str)
59 {
60 FNT_font_t font;
61
62 font = FNT_AutoGet( CL_menuFont );
63
64 return FNT_PredictSize (font, str, true);
65 }
66
ItemName_Draw(menuitem_s * a,FNT_font_t font,const float * color)67 void ItemName_Draw (menuitem_s *a, FNT_font_t font, const float *color)
68 {
69 int text_x, text_y;
70 unsigned int align, cmode;
71
72 if (a->generic.namedraw != NULL)
73 {
74 a->generic.namedraw (a, font);
75 return;
76 }
77
78 if (a->generic.name == NULL)
79 return;
80
81 text_x = Item_GetX (*a);
82 text_y = Item_GetY (*a) + MenuText_UpperMargin (a, font->size);
83
84 if ((a->generic.flags & QMF_BUTTON))
85 {
86 int border_x, width;
87
88 width = CHASELINK(a->generic.lsize).x+CHASELINK(a->generic.rsize).x;
89
90 if (!a->generic.parent->horizontal)
91 {
92 align = FNT_ALIGN_CENTER;
93 text_x = a->generic.parent->x + width/2;
94 border_x = a->generic.parent->x;
95 }
96 else
97 {
98 align = FNT_ALIGN_RIGHT;
99 border_x = text_x - width + RCOLUMN_OFFSET/2;
100 }
101
102 // HACK
103 if (color == dark_color)
104 Menu_DrawHorizBar ("menu/button_border", border_x, Item_GetY(*a)+2, width, font->height*2-4, a->generic.highlight_alpha*a->generic.highlight_alpha);
105 }
106 else if ( a->generic.flags & QMF_RIGHT_COLUMN )
107 {
108 align = FNT_ALIGN_LEFT;
109 }
110 else
111 {
112 if (a->generic.parent->horizontal)
113 {
114 align = FNT_ALIGN_RIGHT;
115 text_x += LCOLUMN_OFFSET;
116 }
117 else
118 {
119 align = FNT_ALIGN_LEFT;
120 text_x = a->generic.parent->x;
121 }
122 }
123
124 if ( a->generic.flags & QMF_STRIP_COLOR || color == highlight_color )
125 cmode = FNT_CMODE_TWO;
126 else
127 cmode = FNT_CMODE_QUAKE_SRS;
128
129 Menu_DrawString (
130 text_x, text_y,
131 a->generic.name, cmode, align, color
132 );
133 }
134
Action_Draw(menuaction_s * a,FNT_font_t font)135 void Action_Draw (menuaction_s *a, FNT_font_t font)
136 {
137 if ( a->generic.itemdraw )
138 a->generic.itemdraw( a, font );
139 }
140
Field_Draw(menufield_s * f,FNT_font_t font)141 void Field_Draw (menufield_s *f, FNT_font_t font)
142 {
143 char tempbuffer[128]="";
144 int x, y;
145
146 y = Item_GetY (*f) + MenuText_UpperMargin (f, font->size);
147 x = Item_GetX (*f) + RCOLUMN_OFFSET;
148
149 strncpy( tempbuffer, f->buffer, f->generic.visible_length);
150
151 Menu_DrawHorizBar (
152 "menu/slide_border", (float)x, (float)y-2.0,
153 (float)(f->generic.visible_length*font->size)+4.0, (float)(font->size)+4.0,
154 f->generic.highlight_alpha*f->generic.highlight_alpha
155 );
156
157 menu_box.x = x+4;
158 menu_box.y = y;
159 menu_box.height = 0;
160 menu_box.width = f->generic.visible_length*font->width;
161 FNT_BoundedPrint (font, f->buffer, FNT_CMODE_QUAKE_SRS, FNT_ALIGN_LEFT, &menu_box, light_color);
162
163 if ( cursor.menuitem == f )
164 {
165 if ( ( ( int ) ( Sys_Milliseconds() / 300 ) ) & 1 )
166 Draw_StretchPic (menu_box.x + menu_box.width - font->size / 8, menu_box.y-1, font->size, font->size+4, "menu/field_cursor");
167 }
168 }
169
Field_Key(int key)170 qboolean Field_Key (int key)
171 {
172 menufield_s *f;
173 extern int keydown[];
174
175 f = cursor.menuitem;
176
177 if (f == NULL || f->generic.type != MTYPE_FIELD || key > 127)
178 return false;
179
180 switch ( key )
181 {
182 case K_KP_SLASH:
183 key = '/';
184 break;
185 case K_KP_MINUS:
186 key = '-';
187 break;
188 case K_KP_PLUS:
189 key = '+';
190 break;
191 case K_KP_HOME:
192 key = '7';
193 break;
194 case K_KP_UPARROW:
195 key = '8';
196 break;
197 case K_KP_PGUP:
198 key = '9';
199 break;
200 case K_KP_LEFTARROW:
201 key = '4';
202 break;
203 case K_KP_5:
204 key = '5';
205 break;
206 case K_KP_RIGHTARROW:
207 key = '6';
208 break;
209 case K_KP_END:
210 key = '1';
211 break;
212 case K_KP_DOWNARROW:
213 key = '2';
214 break;
215 case K_KP_PGDN:
216 key = '3';
217 break;
218 case K_KP_INS:
219 key = '0';
220 break;
221 case K_KP_DEL:
222 key = '.';
223 break;
224 }
225
226 /*
227 ** support pasting from the clipboard
228 */
229 if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
230 ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
231 {
232 char *cbd;
233
234 if ( ( cbd = Sys_GetClipboardData() ) != 0 )
235 {
236 strtok( cbd, "\n\r\b" );
237
238 strncpy( f->buffer, cbd, f->length - 1 );
239 f->cursor = strlen( f->buffer );
240
241 free( cbd );
242 }
243 return true;
244 }
245
246 switch ( key )
247 {
248 case K_KP_LEFTARROW:
249 case K_LEFTARROW:
250 case K_BACKSPACE:
251 if ( f->cursor > 0 )
252 {
253 memmove( &f->buffer[f->cursor-1], &f->buffer[f->cursor], strlen( &f->buffer[f->cursor] ) + 1 );
254 f->cursor--;
255 }
256 break;
257
258 case K_KP_DEL:
259 case K_DEL:
260 memmove( &f->buffer[f->cursor], &f->buffer[f->cursor+1], strlen( &f->buffer[f->cursor+1] ) + 1 );
261 break;
262
263 case K_KP_ENTER:
264 case K_ENTER:
265 case K_ESCAPE:
266 case K_TAB:
267 return false;
268
269 case K_SPACE:
270 default:
271 if ( !isdigit( key ) && ( f->generic.flags & QMF_NUMBERSONLY ) )
272 return false;
273
274 {
275 int maxlength = f->length;
276 if (f->length > sizeof(f->buffer)-2 || f->length == 0)
277 {
278 maxlength = sizeof(f->buffer)-2;
279 }
280 if ( f->cursor < maxlength )
281 {
282 f->buffer[f->cursor++] = key;
283 f->buffer[f->cursor] = 0;
284 }
285 }
286 }
287
288 return true;
289 }
290
_Menu_AddItem(menuframework_s * menu,menucommon_s * item)291 void _Menu_AddItem( menuframework_s *menu, menucommon_s *item )
292 {
293 if ( menu->nitems < MAXMENUITEMS )
294 {
295 menu->items[menu->nitems] = item;
296 ( ( menucommon_s * ) menu->items[menu->nitems] )->parent = menu;
297 menu->nitems++;
298 }
299 }
300
301 // Returns true or false depending on whether the given item is selectable.
Menu_ItemSelectable(menuitem_s * item)302 static inline qboolean Menu_ItemSelectable (menuitem_s *item)
303 {
304 if (item->generic.type == MTYPE_NOT_INTERACTIVE)
305 return false;
306 if (item->generic.type == MTYPE_SUBMENU)
307 return ((menuframework_s *)item)->enable_highlight;
308 return true;
309 }
310
Cursor_SelectItem(menuitem_s * item)311 void Cursor_SelectItem (menuitem_s *item)
312 {
313 cursor.menuitem = item;
314 if (item == NULL)
315 {
316 cursor.menulayer = -1;
317 return;
318 }
319 cursor.menulayer = Cursor_GetLayer ();
320 }
321
322 // Returns the top level node of the menu tree that contains the item
Menu_GetItemTree(menuitem_s * item)323 menuframework_s *Menu_GetItemTree (menuitem_s *item)
324 {
325 if (item->generic.parent == NULL)
326 return (menuframework_s *)item;
327 return Menu_GetItemTree ((menuitem_s *)item->generic.parent);
328 }
329
330 // Returns true if the current cursor item is within the specified menu tree.
Menu_ContainsCursorItem(menuframework_s * menu)331 qboolean Menu_ContainsCursorItem (menuframework_s *menu)
332 {
333 menuitem_s *item;
334
335 item = cursor.menuitem;
336
337 if (item != NULL)
338 {
339 menuframework_s *menu2 = item->generic.parent;
340 while (menu2 != NULL)
341 {
342 if (menu2 == menu)
343 return true;
344 menu2 = menu2->generic.parent;
345 }
346 }
347
348 return false;
349 }
350
351 // Attempt to select the first reasonable menu item in the specified menu tree
352 // as the cursor item. Returns false if unsuccessful (if there are no
353 // selectable menu items within the menu or any of its submenus.) Does nothing
354 // if the cursor item is already within the menu or one of its submenus.
Cursor_SelectMenu(menuframework_s * menu)355 qboolean Cursor_SelectMenu (menuframework_s *menu)
356 {
357 int i;
358 menuitem_s *item;
359
360 if (Menu_ContainsCursorItem (menu))
361 return true;
362
363 refreshCursorLink ();
364
365 if (menu->default_cursor_selection != NULL)
366 {
367 if (Menu_ItemSelectable (menu->default_cursor_selection))
368 {
369 Cursor_SelectItem (menu->default_cursor_selection);
370 return true;
371 }
372 if (menu->default_cursor_selection->generic.type == MTYPE_SUBMENU)
373 {
374 if (Cursor_SelectMenu ((menuframework_s *)menu->default_cursor_selection))
375 return true;
376 }
377 }
378
379 for (i = 0; i < menu->nitems; i++)
380 {
381 item = menu->items[i];
382 if (Menu_ItemSelectable (item))
383 {
384 Cursor_SelectItem (item);
385 return true;
386 }
387 if (item->generic.type == MTYPE_SUBMENU)
388 {
389 menuframework_s *sm = (menuframework_s *)item;
390 if (sm->navagable && Cursor_SelectMenu (sm))
391 return true;
392 }
393 }
394
395 return false;
396 }
397
398 // Find the index of the item within its parent menu.
Menu_ItemIndex(menuitem_s * item)399 int Menu_ItemIndex (menuitem_s *item)
400 {
401 int ret;
402 menuframework_s *menu;
403
404 menu = item->generic.parent;
405
406 for (ret = 0; ret < menu->nitems; ret++)
407 {
408 if (menu->items[ret] == item)
409 return ret;
410 }
411
412 Com_Error (ERR_FATAL, "CAN'T HAPPEN: Item is not in its own parent menu!");
413
414 return 0;
415 }
416
417 // returns true if the item is a submenu that the cursor can advance "into."
CanAdvanceInto(menuitem_s * item,qboolean allow_capture)418 static qboolean CanAdvanceInto (menuitem_s *item, qboolean allow_capture)
419 {
420 if (item->generic.type != MTYPE_SUBMENU)
421 return false;
422
423 // if the submenu will "capture" the cursor, only advance into it if the
424 // tab key has been pressed instead an arrow key.
425 if (!allow_capture && (item->generic.flags & QMF_SUBMENU_CAPTURE))
426 return false;
427
428 // selectable submenus aren't treated like submenus
429 if (Menu_ItemSelectable (item))
430 return false;
431
432 return true;
433 }
434
435 // returns true if the cursor can advance out of the submenu
CanAdvanceOutOf(menuframework_s * submenu,qboolean allow_capture)436 static qboolean CanAdvanceOutOf (menuframework_s *submenu, qboolean allow_capture)
437 {
438 // if the submenu has "captured" the cursor, only advance out of it if the
439 // tab key has been pressed instead of an arrow key.
440 if (!allow_capture && (submenu->generic.flags & QMF_SUBMENU_CAPTURE))
441 return false;
442
443 return true;
444 }
445
446 // Selects either the next or the previous menu item.
Menu_AdvanceCursor(int dir,qboolean allow_capture)447 void Menu_AdvanceCursor (int dir, qboolean allow_capture)
448 {
449 int item_index;
450 menuframework_s *menu;
451 menuitem_s *item, *newitem;
452
453 item = cursor.menuitem;
454
455 if (item == NULL)
456 return;
457
458 menu = item->generic.parent;
459
460 if (item->generic.type == MTYPE_VERT_SCROLLBAR)
461 {
462 menu->yscroll += 2*dir;
463 if (menu->yscroll < 0)
464 menu->yscroll = 0;
465 if (menu->yscroll > menu->maxscroll)
466 menu->yscroll = menu->maxscroll;
467 return;
468 }
469
470 item_index = Menu_ItemIndex (item);
471
472 do
473 {
474 item_index += dir;
475 while (item_index < 0 || item_index >= menu->nitems)
476 {
477 menuframework_s *parent = menu->generic.parent;
478 if (parent == NULL || !CanAdvanceOutOf (menu, allow_capture))
479 {
480 item_index += menu->nitems;
481 item_index %= menu->nitems;
482 }
483 else
484 {
485 item_index = Menu_ItemIndex ((menuitem_s *)menu) + dir;
486 menu = parent;
487 }
488 }
489 newitem = menu->items[item_index];
490 while (CanAdvanceInto (newitem, allow_capture))
491 {
492 menu = (menuframework_s *)newitem;
493 if (dir == 1)
494 item_index = 0;
495 else
496 item_index = menu->nitems-1;
497 newitem = menu->items[item_index];
498 }
499 } while (!Menu_ItemSelectable (newitem));
500
501 Cursor_SelectItem (newitem);
502 }
503
Menu_Center(menuframework_s * menu)504 void Menu_Center( menuframework_s *menu )
505 {
506 menu->y = ( (int)VID_HEIGHT - Menu_TrueHeight(*menu) ) / 2;
507 }
508
SpinControl_MaxLines(menulist_s * s)509 int SpinControl_MaxLines (menulist_s *s)
510 {
511 int i;
512
513 for (i = 0; s->itemnames[i]; i++)
514 {
515 if (strchr( s->itemnames[i], '\n' ))
516 return 2;
517 }
518
519 return 1;
520 }
521
SpinControl_MaxWidth(menulist_s * s)522 int SpinControl_MaxWidth (menulist_s *s)
523 {
524 char buffer[100];
525 int i;
526 int ret = 0;
527
528 for (i = 0; s->itemnames[i]; i++)
529 {
530 if ( !strchr( s->itemnames[i], '\n' ) )
531 {
532 int npix = Menu_PredictSize (s->itemnames[i]);
533 if (npix > ret)
534 ret = npix;
535 }
536 else
537 {
538 int npix;
539 strcpy( buffer, s->itemnames[i] );
540 *strchr( buffer, '\n' ) = 0;
541 npix = Menu_PredictSize (buffer);
542 if (npix > ret)
543 ret = npix;
544 strcpy( buffer, strchr( s->itemnames[i], '\n' ) + 1 );
545 npix = Menu_PredictSize (buffer);
546 if (npix > ret)
547 ret = npix;
548 }
549 }
550
551 return ret;
552 }
553
Menu_Item_LeftSize(menucommon_s * self,FNT_font_t font)554 static inline menuvec2_t Menu_Item_LeftSize (menucommon_s *self, FNT_font_t font)
555 {
556 menuvec2_t ret;
557 ret.x = ret.y = 0;
558
559 if (self->type == MTYPE_SUBMENU)
560 {
561 ret = Menu_GetBorderSize ((menuframework_s *)self);
562 if (self->flags & QMF_SNUG_LEFT)
563 ret.x += CHASELINK(self->parent->lwidth);
564 return ret;
565 }
566
567 if (self->name && !(self->flags & QMF_RIGHT_COLUMN))
568 {
569 ret.x = Menu_PredictSize (self->name);
570 if (self->type != MTYPE_TEXT)
571 ret.x -= LCOLUMN_OFFSET;
572 }
573
574 if ((self->flags & QMF_BUTTON))
575 {
576 ret.y = 2*font->height;
577 if (ret.x < CHASELINK(self->parent->lwidth) && !self->parent->horizontal)
578 ret.x = CHASELINK(self->parent->lwidth);
579 }
580 else
581 {
582 ret.y = font->height;
583 }
584
585 return ret;
586 }
587
Menu_Item_RightSize(menucommon_s * self,FNT_font_t font)588 static inline menuvec2_t Menu_Item_RightSize (menucommon_s *self, FNT_font_t font)
589 {
590 menuvec2_t ret;
591 ret.x = ret.y = 0;
592
593 ret.y = font->height;
594
595 if (self->name && (self->flags & QMF_RIGHT_COLUMN))
596 {
597 ret.x = Menu_PredictSize (self->name);
598 if (self->type != MTYPE_TEXT)
599 ret.x += RCOLUMN_OFFSET;
600 }
601
602 if (self->visible_length != 0)
603 ret.x += font->width * self->visible_length + RCOLUMN_OFFSET;
604
605 switch ( self->type )
606 {
607 case MTYPE_SLIDER:
608 ret.x = font->width * LONGINPUT_SIZE + RCOLUMN_OFFSET;
609 break;
610 case MTYPE_FIELD:
611 ret.y += 4;
612 break;
613 case MTYPE_SPINCONTROL:
614 ret.x += SpinControl_MaxWidth ((menulist_s *)self);
615 ret.y = SpinControl_MaxLines ((menulist_s *)self)*font->height;
616 if (ret.y > font->height)
617 ret.y += font->height-font->size;
618 break;
619 case MTYPE_SUBMENU:
620 ret.y = Menu_TrueHeight(*(menuframework_s *)self);
621 ret.x = CHASELINK(((menuframework_s *)self)->lwidth) +
622 CHASELINK(((menuframework_s *)self)->rwidth);
623 if (self->flags & QMF_SNUG_LEFT)
624 ret.x -= CHASELINK(self->parent->lwidth);
625 {
626 menuvec2_t border = Menu_GetBorderSize ((menuframework_s*)self);
627 if (ret.x < border.x + border.x/2)
628 ret.x = border.x + border.x/2;
629 ret.x += border.x;
630 ret.y += border.y;
631 }
632 break;
633 case MTYPE_ACTION:
634 case MTYPE_TEXT:
635 break;
636 }
637
638 if ((self->flags & QMF_BUTTON) && ret.x < CHASELINK(self->parent->rwidth) && !self->parent->horizontal)
639 ret.x = CHASELINK(self->parent->rwidth);
640
641 return ret;
642 }
643
644 /*
645 =============
646 Menu_AutoArrange
647
648 This section of code is responsible for taking a menu tree and arranging all
649 the submenus and menu items on screen, in such a way that they fit the
650 constraints imposed on them. I'm afraid this is complicated enough to justify
651 a small essay.
652
653 The reason this is so complicated is because of those constraints. Menu width,
654 menu item x, and menu item y are all "linkable" attributes, meaning they can
655 be linked together so they always have the same value. If updated in one
656 place, the updates show up everywhere else. Here's the problem: these linkable
657 attributes depend on each other, sometimes in complex ways.
658
659 For example, take a table with two rows:
660 ITEMA ITEMB ITEMC
661 ITEMD ITEME ITEMF
662 ITEMA and ITEMD's x-coordinates are linked, ITEMB and ITEME's x-coordinates
663 are linked, etc.
664
665 Now the easy way to do this would be to lay out the entirety of row 1, then
666 the entirety of row 2. But suppose you have a case where ITEMA and ITEME are
667 narrow, while ITEMD and ITEMB are wide? Here's what the table looks like
668 before it's been laid out at all:
669 [ITEMA][=ITEMB=][ITEMC]
670 [=ITEMD=][ITEME][ITEMF]
671
672 Laying out the first row goes well. The required amount of space is inserted
673 between ITEMA and ITEMB, and because ITEMB and ITEME are linked, that change
674 is propagated to the second row automatically. Likewise for the space between
675 ITEMB and ITEMC, and the propagation from ITEMC to ITEMF.
676 [ITEMA]---[=ITEMB=]---[ITEMC]
677 [=ITEMD=]-[ITEME]-----[ITEMF]
678
679 Then the second row is laid out. Since ITEMD is wider, the space between it
680 and ITEME isn't enough, so ITEME has to be moved for the space to be
681 sufficient. Because ITEMF is still far enough away from ITEME, it doesn't get
682 moved, and as a result, ITEMC doesn't get moved either! ITEMC's position is no
683 longer valid, so the first row has to be laid out again.
684 [ITEMA]-----[=ITEMB=]-[ITEMC]
685 [=ITEMD=]---[ITEME]---[ITEMF]
686
687 To insure a proper layout, Menu_Arrange is simply called over and over again
688 until no more movement is detected. Actually, the number of calls is limited
689 at 5 to prevent things from getting too out of hand.
690 =============
691 */
692
693 static int changes = 0;
694 #define MENU_INCREASELINK(l,v) \
695 {\
696 if ((l).status == linkstatus_link) \
697 {\
698 if (*((l).ptr) < v) \
699 { \
700 changes++; \
701 *((l).ptr) = v; \
702 } \
703 else \
704 v = *((l).ptr); \
705 }\
706 else if (reset || (l).val < v) \
707 {\
708 changes++;\
709 (l).val = v; \
710 }\
711 else \
712 {\
713 v = (l).val;\
714 }\
715 }
716
Menu_Arrange(menuframework_s * menu,qboolean reset,FNT_font_t font)717 void Menu_Arrange (menuframework_s *menu, qboolean reset, FNT_font_t font)
718 {
719 int i, x, y;
720 int itemheight, horiz_height;
721 menucommon_s *item;
722
723 // TODO: move this into its own function?
724 if (!menu->initialized)
725 {
726 menu->initialized = true;
727 menu->vertical_scrollbar.parent = menu;
728 menu->vertical_scrollbar.type = MTYPE_VERT_SCROLLBAR;
729 }
730
731 x = y = 0;
732
733 horiz_height = 0;
734
735 if (reset)
736 {
737 RESETLINK (menu->rwidth, 0);
738 RESETLINK (menu->lwidth, 0);
739 RESETLINK (menu->height, 0);
740 }
741
742 for (i = 0; i < menu->nitems; i++ )
743 {
744 int lwidth;
745 int rwidth;
746
747 item = ((menucommon_s * )menu->items[i]);
748
749 if (item->type == MTYPE_SUBMENU)
750 {
751 menuvec2_t border;
752 int ntries;
753 int old_nchanges = changes;
754
755 border = Menu_GetBorderSize ((menuframework_s*)item);
756
757 y += border.y;
758 MENU_INCREASELINK (item->y, y);
759 y -= border.y;
760
761 Menu_Arrange ((menuframework_s *)item, reset, font);
762
763 ntries = 0;
764 // attempt to keep repetition localized where possible
765 while (old_nchanges != changes && ++ntries < 3)
766 {
767 old_nchanges = changes;
768 Menu_Arrange ((menuframework_s *)item, false, font);
769 }
770 }
771 else
772 MENU_INCREASELINK (item->y, y);
773
774 if (item->namesizecallback)
775 CHASELINK(item->lsize) = item->namesizecallback (item, font);
776 else
777 CHASELINK(item->lsize) = Menu_Item_LeftSize (item, font);
778 if (item->itemsizecallback)
779 CHASELINK(item->rsize) = item->itemsizecallback (item, font);
780 else
781 CHASELINK(item->rsize) = Menu_Item_RightSize (item, font);
782
783 itemheight = Item_GetHeight(*((menuitem_s *)item));
784 lwidth = CHASELINK(item->lsize).x;
785 rwidth = CHASELINK(item->rsize).x;
786
787 if (menu->horizontal)
788 {
789 if (horiz_height < itemheight)
790 horiz_height = itemheight;
791 }
792 else
793 y += itemheight;
794
795 if (menu->horizontal)
796 {
797 if (i == 0)
798 MENU_INCREASELINK (menu->lwidth, lwidth)
799 else
800 x += lwidth + RCOLUMN_OFFSET - LCOLUMN_OFFSET;
801 MENU_INCREASELINK (item->x, x)
802 x += rwidth;
803 }
804 else
805 {
806 MENU_INCREASELINK (menu->lwidth, lwidth);
807 MENU_INCREASELINK (menu->rwidth, rwidth);
808 }
809 }
810
811 if (menu->horizontal)
812 {
813 MENU_INCREASELINK (menu->rwidth, x);
814 MENU_INCREASELINK (menu->height, horiz_height);
815 }
816 else
817 MENU_INCREASELINK (menu->height, y);
818
819 if (!menu->horizontal && menu->maxlines != 0 && menu->maxlines < menu->nitems)
820 menu->maxheight = CHASELINK (((menucommon_s*)menu->items[menu->maxlines])->y);
821
822 menu->maxscroll = CHASELINK(menu->height) - menu->maxheight;
823 menu->scroll_range = (float)menu->maxheight - font->size/2.0;
824 menu->scrollbar_size = menu->scroll_range - menu->maxscroll;
825 if (menu->scrollbar_size < font->size)
826 menu->scrollbar_size = font->size;
827 menu->scroll_top = (float)menu->y+font->size/4.0;
828 }
829
Menu_AutoArrange(menuframework_s * menu)830 void Menu_AutoArrange (menuframework_s *menu)
831 {
832 int ntries;
833 FNT_font_t font;
834
835 font = FNT_AutoGet( CL_menuFont );
836
837 Menu_Arrange (menu, true, font);
838 ntries = 0;
839 while (changes && ++ntries < 5)
840 {
841 changes = 0;
842 Menu_Arrange (menu, false, font);
843 }
844 }
845
Item_ScrollVisible(menuitem_s * item)846 static inline qboolean Item_ScrollVisible (menuitem_s *item)
847 {
848 menuframework_s *menu = item->generic.parent;
849 // scrolling disabled on this item
850 if (menu->maxheight == 0)
851 return true;
852 if (CHASELINK(item->generic.y)-menu->yscroll < 0)
853 return false;
854 if (CHASELINK(item->generic.y)-menu->yscroll + Item_GetHeight(*item) > menu->maxheight)
855 return false;
856 return true;
857 }
858
Item_UpdateHighlightAlpha(menuitem_s * item)859 void Item_UpdateHighlightAlpha (menuitem_s *item)
860 {
861 if (cursor.menuitem == item)
862 {
863 item->generic.highlight_alpha += cls.frametime*0.5f;
864 if (item->generic.highlight_alpha > 1.0)
865 item->generic.highlight_alpha = 1.0;
866 }
867 else
868 {
869 item->generic.highlight_alpha -= cls.frametime*0.5f;
870 if (item->generic.highlight_alpha < 0.9)
871 item->generic.highlight_alpha = 0.9;
872 }
873 }
874
Menu_Draw(menuframework_s * menu,FNT_font_t font)875 void Menu_Draw (menuframework_s *menu, FNT_font_t font)
876 {
877 int i;
878 menuitem_s *item;
879
880 if (menu->bordertexture != NULL)
881 Menu_DrawBorder (menu, menu->bordertitle, menu->bordertexture);
882
883 // may get covered up later by a higher-priority status bar
884 if (menu->num_apply_pending != 0)
885 Menu_DrawStatusBar ("Some changes must be applied!\n");
886 else
887 Menu_DrawStatusBar (menu->statusbar);
888
889 Item_UpdateHighlightAlpha ((menuitem_s *)&menu->vertical_scrollbar);
890
891 /*
892 ** draw contents
893 */
894 for ( i = 0; i < menu->nitems; i++ )
895 {
896 item = ((menuitem_s * )menu->items[i]);
897
898 if (!Item_ScrollVisible (item))
899 continue;
900
901 Item_UpdateHighlightAlpha (item);
902
903 // TODO: cleaner method
904 if (item->generic.type == MTYPE_NOT_INTERACTIVE)
905 {
906 if (item->generic.itemdraw != NULL)
907 item->generic.itemdraw (item, font);
908
909 if (item->generic.namedraw != NULL)
910 item->generic.namedraw (item, font);
911 else
912 Label_Draw ((menutxt_s *) menu->items[i], font, dark_color);
913 }
914 else if (item->generic.type != MTYPE_SUBMENU)
915 {
916 ItemName_Draw ((menuitem_s *) menu->items[i], font, dark_color);
917 }
918
919 switch ( item->generic.type )
920 {
921 case MTYPE_FIELD:
922 Field_Draw ((menufield_s *) menu->items[i], font);
923 break;
924 case MTYPE_SLIDER:
925 Slider_Draw ((menuslider_s *) menu->items[i], font);
926 break;
927 case MTYPE_SPINCONTROL:
928 SpinControl_Draw ((menulist_s *) menu->items[i], font);
929 break;
930 case MTYPE_ACTION:
931 Action_Draw ((menuaction_s *) menu->items[i], font);
932 break;
933 case MTYPE_TEXT:
934 break;
935 case MTYPE_SUBMENU:
936 SubMenu_Draw ((menuframework_s *) menu->items[i], font);
937 break;
938 }
939 }
940 }
941
Cursor_MouseSelectItem(menuitem_s * item)942 static void Cursor_MouseSelectItem (menuitem_s *item)
943 {
944 menuitem_s *lastitem = (menuitem_s *)cursor.menuitem;
945
946 // Selected a new item-- reset double-click count
947 if (lastitem != item)
948 {
949 memset (cursor.buttonclicks, 0, sizeof(cursor.buttonclicks));
950 memset (cursor.buttontime, 0, sizeof(cursor.buttontime));
951 }
952
953 Cursor_SelectItem (item);
954 cursor.mouseaction = false;
955 }
956
Menu_AssignCursor(menuframework_s * menu)957 void Menu_AssignCursor (menuframework_s *menu)
958 {
959 int i;
960 float right;
961 menuitem_s *item;
962
963 if (!menu->navagable || !cursor.mouseaction)
964 return;
965
966 right = menu->x + CHASELINK(menu->rwidth) + CHASELINK(menu->lwidth);
967
968 if (menu->maxheight != 0 && CHASELINK(menu->height) > menu->maxheight && cursor.x > right)
969 {
970 // select the scrollbar
971 item = &menu->vertical_scrollbar;
972 Cursor_MouseSelectItem (item);
973 }
974 else for ( i = 0; i < menu->nitems; i++ )
975 {
976 int mincoord, maxcoord;
977
978 item = ((menuitem_s * )menu->items[i]);
979
980 if (!item)
981 continue;
982
983 if (menu->horizontal)
984 {
985 maxcoord = mincoord = Item_GetX (*item);
986 maxcoord += CHASELINK(item->generic.rsize).x;
987 mincoord -= CHASELINK(item->generic.lsize).x;
988 if (cursor.x < mincoord || cursor.x > maxcoord)
989 continue;
990 }
991 else
992 {
993 maxcoord = mincoord = Item_GetY (*item);
994 maxcoord += Item_GetHeight (*item);
995 if (cursor.y < mincoord || cursor.y > maxcoord)
996 continue;
997 }
998
999 if (item->generic.type == MTYPE_SUBMENU && ((menuframework_s *)item)->navagable)
1000 {
1001 // navagable menus should have at least one selectable item in
1002 // them.
1003 Menu_AssignCursor ((menuframework_s *)item);
1004 return;
1005 }
1006
1007 if (!Menu_ItemSelectable (item))
1008 continue;
1009
1010 // We've found a valid candiate for selection
1011 Cursor_MouseSelectItem (item);
1012 return;
1013 }
1014 }
1015
1016 void Menu_DrawHighlightItem (menuitem_s *item);
1017
1018 // Draws only the item labels, and draws everything highlighted. For cases
1019 // where the menu itself is being used as a sort of complex widget.
Menu_DrawHighlightMenu(menuframework_s * menu,FNT_font_t font)1020 void Menu_DrawHighlightMenu (menuframework_s *menu, FNT_font_t font)
1021 {
1022 int i;
1023 menuitem_s *item;
1024
1025 for ( i = 0; i < menu->nitems; i++ )
1026 {
1027 item = ((menuitem_s * )menu->items[i]);
1028
1029 Menu_DrawHighlightItem (item);
1030 }
1031 }
1032
Menu_DrawHighlightItem(menuitem_s * item)1033 void Menu_DrawHighlightItem (menuitem_s *item)
1034 {
1035 FNT_font_t font = FNT_AutoGet (CL_menuFont);
1036
1037 if (!Item_ScrollVisible (item))
1038 return;
1039
1040 if (item->generic.cursorcallback)
1041 item->generic.cursorcallback (item, font);
1042
1043 // highlighting
1044 if (item->generic.cursordraw != NULL)
1045 {
1046 item->generic.cursordraw( item, font );
1047 return;
1048 }
1049
1050 if (item->generic.namedraw != NULL)
1051 return;
1052
1053 if (item->generic.type == MTYPE_SUBMENU && ((menuframework_s *)item)->enable_highlight)
1054 Menu_DrawHighlightMenu ((menuframework_s *)item, font);
1055 else if (item->generic.type == MTYPE_TEXT)
1056 Label_Draw (item, font, highlight_color);
1057 else
1058 ItemName_Draw (item, font, highlight_color);
1059 }
1060
Menu_DrawHighlight(void)1061 void Menu_DrawHighlight (void)
1062 {
1063 menuframework_s *menu;
1064 menuitem_s *item = cursor.menuitem;
1065
1066 if (item == NULL || item->generic.type == MTYPE_VERT_SCROLLBAR)
1067 return;
1068
1069 menu = item->generic.parent;
1070
1071 // Scrolling - make sure the selected item is entirely on screen if
1072 // possible. TODO: add smoothing?
1073 if (menu->maxheight != 0)
1074 {
1075 int newscroll;
1076 int y = CHASELINK(item->generic.y);
1077 newscroll = clamp (menu->yscroll, y + Item_GetHeight(*item) - menu->maxheight, y);
1078 if (newscroll != menu->yscroll)
1079 {
1080 menu->yscroll = newscroll;
1081 return; // we'll draw it next frame - not in the right position now
1082 }
1083 }
1084
1085 if (item->generic.cursordraw == NULL && menu->cursordraw != NULL)
1086 {
1087 menu->cursordraw( menu );
1088 return;
1089 }
1090
1091 if (item->generic.type == MTYPE_SUBMENU)
1092 Menu_DrawBorder ((menuframework_s *)item, NULL, "menu/sm_");
1093
1094 // no actions you can do with these types, so use them only for scrolling
1095 // and then return
1096 if (item->generic.type == MTYPE_NOT_INTERACTIVE)
1097 return;
1098
1099 Menu_DrawHighlightItem (item);
1100
1101 if ( item->generic.statusbar )
1102 Menu_DrawStatusBar( item->generic.statusbar );
1103
1104 if ( item->generic.tooltip )
1105 Menu_DrawToolTip (item);
1106 }
1107
1108 // needed because global_menu_xoffset must be added to only the top level of
1109 // any menu tree.
Screen_Draw(menuframework_s * screen,menuvec2_t offset)1110 void Screen_Draw (menuframework_s *screen, menuvec2_t offset)
1111 {
1112 FNT_font_t font = FNT_AutoGet (CL_menuFont);
1113 screen->x = offset.x;
1114 //TODO: figure out what to do about centered windows
1115 /* screen->y = offset.y;*/
1116 Menu_AutoArrange (screen);
1117 Menu_Draw (screen, font);
1118 }
1119
Menu_GetBorderSize(menuframework_s * s)1120 static menuvec2_t Menu_GetBorderSize (menuframework_s *s)
1121 {
1122 char topcorner_name[MAX_QPATH];
1123 menuvec2_t ret;
1124 FNT_font_t font;
1125
1126 font = FNT_AutoGet( CL_menuFont );
1127
1128 ret.x = ret.y = 0;
1129
1130 if (s->bordertexture != NULL)
1131 {
1132 Com_sprintf (topcorner_name, MAX_QPATH, "%stopcorner.tga", s->bordertexture);
1133 Draw_GetPicSize (&ret.x, &ret.y, topcorner_name );
1134 ret.x = (int)((float)ret.x/64.0*(float)font->size*4.0);
1135 ret.y = (int)((float)ret.y/64.0*(float)font->size*4.0);
1136 ret.x -= font->size;
1137 ret.y -= font->size;
1138 }
1139
1140 return ret;
1141 }
1142
Menu_DrawBox(int x,int y,int w,int h,float alpha,const char * title,const char * prefix)1143 void Menu_DrawBox (int x, int y, int w, int h, float alpha, const char *title, const char *prefix)
1144 {
1145 char topcorner_name[MAX_QPATH];
1146 char bottomcorner_name[MAX_QPATH];
1147 char top_name[MAX_QPATH];
1148 char bottom_name[MAX_QPATH];
1149 char side_name[MAX_QPATH];
1150 char background_name[MAX_QPATH];
1151 int _tile_w, _tile_h;
1152 float tile_w, tile_h;
1153 FNT_font_t font;
1154
1155 font = FNT_AutoGet( CL_menuFont );
1156
1157 Com_sprintf (topcorner_name, MAX_QPATH, "%stopcorner.tga", prefix);
1158 Com_sprintf (bottomcorner_name, MAX_QPATH, "%sbottomcorner.tga", prefix);
1159 Com_sprintf (top_name, MAX_QPATH, "%stop.tga", prefix);
1160 Com_sprintf (bottom_name, MAX_QPATH, "%sbottom.tga", prefix);
1161 Com_sprintf (side_name, MAX_QPATH, "%sside.tga", prefix);
1162 Com_sprintf (background_name, MAX_QPATH, "%sbackground.tga", prefix);
1163
1164 // assume all tiles are the same size
1165 Draw_GetPicSize (&_tile_w, &_tile_h, topcorner_name );
1166
1167 tile_w = (float)_tile_w/64.0*(float)font->size*4.0;
1168 tile_h = (float)_tile_h/64.0*(float)font->size*4.0;
1169
1170 // make room for the scrollbar
1171 if (!strcmp (prefix, "menu/sm_"))
1172 {
1173 w += font->size;
1174 x -= font->size/2;
1175 }
1176
1177 if (w < tile_w)
1178 {
1179 w = tile_w;
1180 }
1181
1182 if (h < tile_h)
1183 {
1184 h = tile_h;
1185 }
1186
1187 // hacky stuff to make things look right
1188 if (strcmp (prefix, "menu/sm_"))
1189 {
1190 x -= tile_w/2;
1191 w += tile_w;
1192 }
1193 if (!strcmp (prefix, "menu/m_"))
1194 {
1195 y -= tile_h/8;
1196 if (h > tile_h)
1197 h += tile_h/4;
1198 }
1199
1200
1201 Draw_AlphaStretchTilingPic( x-tile_w/2, y-tile_h/2, tile_w, tile_h, topcorner_name, alpha );
1202 Draw_AlphaStretchTilingPic( x+w+tile_w/2, y-tile_h/2, -tile_w, tile_h, topcorner_name, alpha );
1203 Draw_AlphaStretchTilingPic( x-tile_w/2, y+h-tile_h/2, tile_w, tile_h, bottomcorner_name, alpha );
1204 Draw_AlphaStretchTilingPic( x+w+tile_w/2, y+h-tile_h/2, -tile_w, tile_h, bottomcorner_name, alpha );
1205 if (w > tile_w)
1206 {
1207 Draw_AlphaStretchTilingPic( x+tile_w/2, y-tile_h/2, w-tile_w, tile_h, top_name, alpha );
1208 Draw_AlphaStretchTilingPic( x+tile_w/2, y+h-tile_h/2, w-tile_w, tile_h, bottom_name, alpha );
1209 }
1210
1211 if (h > tile_h)
1212 {
1213 Draw_AlphaStretchTilingPic( x-tile_w/2, y+tile_h/2, tile_w, h-tile_h, side_name, alpha );
1214 Draw_AlphaStretchTilingPic( x+w+tile_w/2, y+tile_h/2, -tile_w, h-tile_h, side_name, alpha );
1215 if (w > tile_w)
1216 Draw_AlphaStretchTilingPic( x+tile_w/2, y+tile_h/2, w-tile_w, h-tile_h, background_name, alpha );
1217 }
1218
1219 if (title != NULL)
1220 {
1221 int i;
1222 int textwidth = Menu_PredictSize (title);
1223 Menu_DrawHorizBar ("menu/slide_border", x+w/2-textwidth/2-2, y-font->size-2, textwidth+4, font->size+4, 1);
1224 // Redraw multiple times to get a bold effect
1225 for (i = 0; i < 4; i++)
1226 Menu_DrawString (x+w/2, y-font->size, title, FNT_CMODE_QUAKE, FNT_ALIGN_CENTER, highlight_color);
1227 }
1228 }
1229
1230
Menu_DrawVertBar(const char * pathbase,float x,float y,float h,float base_size,float alpha)1231 void Menu_DrawVertBar (const char *pathbase, float x, float y, float h, float base_size, float alpha)
1232 {
1233 char scratch[MAX_QPATH];
1234
1235 Com_sprintf( scratch, sizeof( scratch ), "%s%s", pathbase, "_end");
1236
1237 Draw_AlphaStretchTilingPic (x, y-base_size/2.0, base_size, base_size, scratch, alpha);
1238 Draw_AlphaStretchTilingPic (x+base_size, y+h+base_size/2.0, -base_size, -base_size, scratch, alpha);
1239 if (h > base_size)
1240 Draw_AlphaStretchTilingPic (x, y+base_size/2.0, base_size, h-base_size, pathbase, alpha);
1241 }
1242
Menu_DrawHorizBar(const char * pathbase,float x,float y,float w,float base_size,float alpha)1243 void Menu_DrawHorizBar (const char *pathbase, float x, float y, float w, float base_size, float alpha)
1244 {
1245 char scratch[MAX_QPATH];
1246
1247 Com_sprintf( scratch, sizeof( scratch ), "%s%s", pathbase, "_end");
1248
1249 Draw_AlphaStretchTilingPic (x-base_size/2.0, y, base_size, base_size, scratch, alpha);
1250 Draw_AlphaStretchTilingPic (x+w+base_size/2.0, y+base_size, -base_size, -base_size, scratch, alpha);
1251 if (w > base_size)
1252 Draw_AlphaStretchTilingPic (x+base_size/2.0, y, w-base_size, base_size, pathbase, alpha);
1253 }
1254
Menu_DrawScrollbar(menuframework_s * menu)1255 void Menu_DrawScrollbar (menuframework_s *menu)
1256 {
1257 float scrollbar_pos;
1258 float charscale, right;
1259 float alpha;
1260 FNT_font_t font;
1261
1262 font = FNT_AutoGet( CL_menuFont );
1263 charscale = font->size;
1264
1265 if (menu->maxheight == 0 || CHASELINK(menu->height) <= menu->maxheight)
1266 return;
1267
1268 right = menu->x + CHASELINK(menu->rwidth) + CHASELINK(menu->lwidth) + charscale/2.0;
1269
1270 scrollbar_pos = menu->scroll_top + (float)menu->yscroll*(menu->scroll_range-menu->scrollbar_size)/menu->maxscroll;
1271 alpha = menu->vertical_scrollbar.highlight_alpha;
1272
1273 Menu_DrawVertBar ("menu/scroll_border", right, menu->y, menu->maxheight, charscale, alpha*alpha);
1274 Menu_DrawVertBar ("menu/scroll_cursor", right, scrollbar_pos, menu->scrollbar_size, charscale, alpha);
1275 }
1276
Menu_DrawBorder(menuframework_s * menu,const char * title,const char * prefix)1277 void Menu_DrawBorder (menuframework_s *menu, const char *title, const char *prefix)
1278 {
1279 int height, width;
1280
1281 height = CHASELINK(menu->height);
1282
1283 if (menu->maxheight != 0 && height > menu->maxheight)
1284 height = menu->maxheight;
1285
1286 if (strcmp (prefix, "menu/m_"))
1287 {
1288 menu->borderalpha = 0.50;
1289 }
1290 else if (Menu_ContainsCursorItem (menu))
1291 {
1292 menu->borderalpha += cls.frametime*2;
1293 if (menu->borderalpha > 1.0)
1294 menu->borderalpha = 1.0;
1295 }
1296 else
1297 {
1298 menu->borderalpha -= cls.frametime*2;
1299 if (menu->borderalpha < 0.5)
1300 menu->borderalpha = 0.5;
1301 }
1302
1303 width = CHASELINK(menu->lwidth) + CHASELINK(menu->rwidth);
1304 Menu_DrawBox (menu->x, menu->y, width, height, menu->borderalpha, title, prefix);
1305 }
1306
Menu_DrawToolTip(const menuitem_s * item)1307 void Menu_DrawToolTip (const menuitem_s *item)
1308 {
1309
1310 int x, y;
1311 int width;
1312 FNT_font_t font;
1313
1314 font = FNT_AutoGet (CL_menuFont);
1315
1316 width = Menu_PredictSize (item->generic.tooltip);
1317
1318 x = clamp (cursor.x, Item_GetX (*item) - width, VID_WIDTH - width);
1319
1320 y = clamp (cursor.y - font->size - 4, Item_GetY(*item) - font->size, Item_GetY(*item));
1321
1322 Menu_DrawHorizBar ("menu/slide_border", x-2, y, width+4, font->size+4, 1);
1323 Menu_DrawString (
1324 x, y,
1325 item->generic.tooltip, FNT_CMODE_QUAKE_SRS, FNT_ALIGN_LEFT,
1326 light_color
1327 );
1328 }
1329
Menu_DrawString(int x,int y,const char * string,unsigned int cmode,unsigned int align,const float * color)1330 void Menu_DrawString (int x, int y, const char *string, unsigned int cmode, unsigned int align, const float *color)
1331 {
1332 FNT_font_t font;
1333
1334 font = FNT_AutoGet( CL_menuFont );
1335
1336 if (align == FNT_ALIGN_RIGHT)
1337 {
1338 menu_box.x = 0;
1339 menu_box.width = x;
1340 }
1341 else if (align == FNT_ALIGN_CENTER)
1342 {
1343 //width only needs to be an upper bound
1344 int width = strlen(string)*font->size;
1345 menu_box.x = x-width/2;
1346 menu_box.width = width;
1347 }
1348 else
1349 {
1350 menu_box.x = x;
1351 menu_box.width = 0;
1352 }
1353
1354 menu_box.y = y;
1355 menu_box.height = 0;
1356
1357 FNT_BoundedPrint (font, string, cmode, align, &menu_box, color);
1358
1359 if (align == FNT_ALIGN_RIGHT)
1360 menu_box.x = x-menu_box.width;
1361 else if (align == FNT_ALIGN_CENTER)
1362 menu_box.x = x-menu_box.width/2;
1363 }
1364
Menu_DrawStatusBar(const char * string)1365 void Menu_DrawStatusBar( const char *string )
1366 {
1367 FNT_font_t font;
1368
1369 font = FNT_AutoGet( CL_menuFont );
1370
1371 if ( string )
1372 {
1373 Draw_Fill (0, VID_HEIGHT-font->size-10, VID_WIDTH, font->size+10, RGBA(0.25, 0.25, 0.25, 1));
1374 Menu_DrawString (VID_WIDTH/2, VID_HEIGHT-font->size-5, string, FNT_CMODE_QUAKE, FNT_ALIGN_CENTER, light_color);
1375 }
1376 }
1377
Menu_ActivateItem(menuitem_s * item)1378 void Menu_ActivateItem (menuitem_s *item)
1379 {
1380 if (item == NULL || item->generic.callback == NULL)
1381 return;
1382 if ((item->generic.flags & QMF_ACTION_WAIT))
1383 {
1384 if (!item->generic.apply_pending)
1385 {
1386 item->generic.apply_pending = true;
1387 Menu_GetItemTree (item)->num_apply_pending++;
1388 }
1389 }
1390 else
1391 {
1392 item->generic.callback (item);
1393 }
1394 }
1395
Menu_ApplyItem(menuitem_s * item)1396 void Menu_ApplyItem (menuitem_s *item)
1397 {
1398 if (item != NULL && item->generic.callback != NULL && (item->generic.flags & QMF_ACTION_WAIT) && item->generic.apply_pending)
1399 {
1400 Menu_GetItemTree (item)->num_apply_pending--;
1401 item->generic.callback (item);
1402 item->generic.apply_pending = false;
1403 }
1404 }
1405
Menu_ApplyMenu(menuframework_s * menu)1406 void Menu_ApplyMenu (menuframework_s *menu)
1407 {
1408 int i;
1409 menuitem_s *item;
1410
1411 for ( i = 0; i < menu->nitems; i++ )
1412 {
1413 item = ((menuitem_s * )menu->items[i]);
1414
1415 if (item->generic.type == MTYPE_SUBMENU)
1416 Menu_ApplyMenu ((menuframework_s *)item);
1417 else
1418 Menu_ApplyItem (item);
1419 }
1420 }
1421
Menu_SetStatusBar(menuframework_s * m,const char * string)1422 void Menu_SetStatusBar( menuframework_s *m, const char *string )
1423 {
1424 m->statusbar = string;
1425 }
1426
Menu_SlideItem(int dir)1427 void Menu_SlideItem (int dir)
1428 {
1429 menucommon_s *item = cursor.menuitem;
1430
1431 if ( item )
1432 {
1433 switch ( item->type )
1434 {
1435 case MTYPE_SLIDER:
1436 Slider_DoSlide( ( menuslider_s * ) item, dir );
1437 break;
1438 case MTYPE_SPINCONTROL:
1439 SpinControl_DoSlide( ( menulist_s * ) item, dir );
1440 break;
1441 }
1442 }
1443 }
1444
Label_Draw(menutxt_s * s,FNT_font_t font,const float * color)1445 void Label_Draw (menutxt_s *s, FNT_font_t font, const float *color)
1446 {
1447 unsigned int align;
1448 unsigned int cmode;
1449
1450 if ( s->generic.name == NULL)
1451 return;
1452
1453 if ( s->generic.flags & QMF_RIGHT_COLUMN )
1454 align = FNT_ALIGN_LEFT;
1455 else
1456 align = FNT_ALIGN_RIGHT;
1457
1458 cmode = FNT_CMODE_QUAKE_SRS;
1459 if (color == highlight_color)
1460 cmode = FNT_CMODE_TWO;
1461
1462 Menu_DrawString (
1463 Item_GetX (*s), Item_GetY (*s) + MenuText_UpperMargin (s, font->size),
1464 s->generic.name, cmode, align, color
1465 );
1466 }
1467
Slider_DoSlide(menuslider_s * s,int dir)1468 void Slider_DoSlide( menuslider_s *s, int dir )
1469 {
1470 s->curvalue += dir;
1471
1472 if ( s->curvalue > s->maxvalue )
1473 s->curvalue = s->maxvalue;
1474 else if ( s->curvalue < s->minvalue )
1475 s->curvalue = s->minvalue;
1476
1477 Menu_ActivateItem (s);
1478 }
1479
Slider_Draw(menuslider_s * s,FNT_font_t font)1480 void Slider_Draw (menuslider_s *s, FNT_font_t font)
1481 {
1482 float maxscroll, curscroll, scroll_range, cursor_size, x, y, width;
1483 float charscale;
1484
1485 charscale = font->size;
1486
1487 x = Item_GetX (*s) + RCOLUMN_OFFSET;
1488 y = Item_GetY (*s) + MenuText_UpperMargin (s, font->size);
1489
1490 curscroll = s->curvalue - s->minvalue;
1491 maxscroll = s->maxvalue - s->minvalue;
1492
1493 s->range = (float) curscroll / ( float ) maxscroll;
1494
1495 width = charscale * (float)LONGINPUT_SIZE;
1496 scroll_range = width-charscale/2.0;
1497 cursor_size = charscale;
1498
1499 Menu_DrawHorizBar (
1500 "menu/slide_border", x, y, width, charscale,
1501 s->generic.highlight_alpha*s->generic.highlight_alpha
1502 );
1503 Menu_DrawHorizBar (
1504 "menu/slide_cursor", x+charscale/4.0+s->range*(scroll_range-cursor_size), y,
1505 cursor_size, charscale,
1506 s->generic.highlight_alpha
1507 );
1508 }
1509
SpinControl_DoSlide(menulist_s * s,int dir)1510 void SpinControl_DoSlide( menulist_s *s, int dir )
1511 {
1512 int i;
1513 s->curvalue += dir;
1514
1515 if ( s->curvalue < 0 )
1516 {
1517 if (s->generic.flags & QMF_ALLOW_WRAP)
1518 {
1519 for (i = 0; s->itemnames[i]; i++)
1520 continue;
1521 s->curvalue += i;
1522 }
1523 else
1524 s->curvalue = 0;
1525 }
1526 else if ( s->itemnames[s->curvalue] == 0 )
1527 {
1528 if (s->generic.flags & QMF_ALLOW_WRAP)
1529 s->curvalue = 0;
1530 else
1531 s->curvalue--;
1532 }
1533
1534 Menu_ActivateItem (s);
1535 }
1536
SpinControl_Draw(menulist_s * s,FNT_font_t font)1537 void SpinControl_Draw (menulist_s *s, FNT_font_t font)
1538 {
1539 char buffer[100];
1540 int item_x, item_y;
1541
1542 item_x = Item_GetX (*s) + RCOLUMN_OFFSET;
1543 item_y = Item_GetY (*s) + MenuText_UpperMargin (s, SpinControl_MaxLines (s)*font->size);
1544
1545 if (s->generic.namedraw == NULL && s->generic.name != NULL && s->generic.flags & QMF_RIGHT_COLUMN)
1546 {
1547 // Both the name and item go in the right column.
1548 item_x += Menu_PredictSize (s->generic.name);
1549 }
1550
1551 if (s->generic.itemdraw != NULL)
1552 {
1553 s->generic.itemdraw (s, font);
1554 }
1555 else if ( !strchr( s->itemnames[s->curvalue], '\n' ) )
1556 {
1557 Menu_DrawString (
1558 item_x, item_y,
1559 s->itemnames[s->curvalue], FNT_CMODE_QUAKE_SRS, FNT_ALIGN_LEFT,
1560 light_color
1561 );
1562 }
1563 else
1564 {
1565 strcpy( buffer, s->itemnames[s->curvalue] );
1566 *strchr( buffer, '\n' ) = 0;
1567 Menu_DrawString (
1568 item_x, item_y,
1569 buffer, FNT_CMODE_QUAKE_SRS, FNT_ALIGN_LEFT,
1570 light_color
1571 );
1572 strcpy( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1 );
1573 Menu_DrawString (
1574 item_x, menu_box.y + font->height,
1575 buffer, FNT_CMODE_QUAKE_SRS, FNT_ALIGN_LEFT,
1576 light_color
1577 );
1578 }
1579 }
1580
SubMenu_Draw(menuframework_s * sm,FNT_font_t font)1581 void SubMenu_Draw (menuframework_s *sm, FNT_font_t font)
1582 {
1583 sm->x = Item_GetX (*sm);
1584 if (sm->generic.flags & QMF_SNUG_LEFT)
1585 sm->x -= CHASELINK(sm->generic.parent->lwidth);
1586 sm->y = Item_GetY (*sm);
1587 if (sm->navagable)
1588 Menu_DrawScrollbar (sm);
1589 Menu_Draw (sm, font);
1590 }
1591
1592 // utility functions
1593
Menu_MakeTable(menuframework_s * menu,int nrows,int ncolumns,size_t * celltype_size,menuframework_s * header,menuframework_s * rows,void * columns,const char ** contents)1594 void Menu_MakeTable (menuframework_s *menu, int nrows, int ncolumns, size_t *celltype_size, menuframework_s *header, menuframework_s *rows, void *columns, const char **contents)
1595 {
1596 int i, j;
1597 menuframework_s *cur_row = rows;
1598 // char because the measurements in celltype_size are in bytes
1599 char *cur_cell_p = columns;
1600
1601 menu->nitems = 0;
1602 for (i = 0; i < nrows; i++)
1603 {
1604 cur_row->nitems = 0;
1605 cur_row->generic.type = MTYPE_SUBMENU;
1606 cur_row->horizontal = true;
1607 cur_row->navagable = false;
1608 cur_row->enable_highlight = true;
1609 if (cur_row != header)
1610 {
1611 LINK(header->lwidth, cur_row->lwidth);
1612 LINK(header->rwidth, cur_row->rwidth);
1613 }
1614 for (j = 0; j < ncolumns; j++)
1615 {
1616 menuitem_s *cur_cell = (menuitem_s *)cur_cell_p;
1617 if (cur_row != header)
1618 {
1619 menucommon_s *header_cell = (menucommon_s *)header->items[j];
1620 memcpy (cur_cell, header_cell, sizeof (menucommon_s));
1621 //reset after memcpy
1622 cur_cell->generic.x.status = linkstatus_literal;
1623 LINK(header_cell->x, cur_cell->generic.x);
1624 }
1625 cur_cell->generic.name = contents[i*ncolumns+j];
1626 Menu_AddItem (cur_row, cur_cell);
1627 cur_cell_p += celltype_size[j];
1628 }
1629 Menu_AddItem (menu, cur_row);
1630 cur_row++;
1631 }
1632 Menu_AutoArrange (menu);
1633 }
1634