1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 #include "client.h"
21 #include "qmenu.h"
22
23 static void Action_DoEnter( menuaction_s *a );
24 static void Action_Draw( menuaction_s *a );
25 static void Menu_DrawStatusBar( const char *string );
26 //static void Menulist_DoEnter( menulist_s *l );
27 static void MenuList_Draw( menulist_s *l );
28 static void Separator_Draw( menuseparator_s *s );
29 static void Slider_DoSlide( menuslider_s *s, int dir );
30 static void Slider_Draw( menuslider_s *s );
31 //static void SpinControl_DoEnter( menulist_s *s );
32 static void SpinControl_Draw( menulist_s *s );
33 static void SpinControl_DoSlide( menulist_s *s, int dir );
34
35 #define RCOLUMN_OFFSET 16
36 #define LCOLUMN_OFFSET -16
37
38 extern refexport_t re;
39 extern viddef_t viddef;
40
41 #define VID_WIDTH viddef.width
42 #define VID_HEIGHT viddef.height
43
44 #define Draw_Char re.DrawChar
45 #define Draw_Fill re.DrawFill
46
Action_DoEnter(menuaction_s * a)47 void Action_DoEnter( menuaction_s *a )
48 {
49 if ( a->generic.callback )
50 a->generic.callback( a );
51 }
52
Action_Draw(menuaction_s * a)53 void Action_Draw( menuaction_s *a )
54 {
55 if ( a->generic.flags & QMF_LEFT_JUSTIFY )
56 {
57 if ( a->generic.flags & QMF_GRAYED )
58 Menu_DrawStringDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
59 else
60 Menu_DrawString( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
61 }
62 else
63 {
64 if ( a->generic.flags & QMF_GRAYED )
65 Menu_DrawStringR2LDark( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
66 else
67 Menu_DrawStringR2L( a->generic.x + a->generic.parent->x + LCOLUMN_OFFSET, a->generic.y + a->generic.parent->y, a->generic.name );
68 }
69 if ( a->generic.ownerdraw )
70 a->generic.ownerdraw( a );
71 }
72
Field_DoEnter(menufield_s * f)73 qboolean Field_DoEnter( menufield_s *f )
74 {
75 if ( f->generic.callback )
76 {
77 f->generic.callback( f );
78 return true;
79 }
80 return false;
81 }
82
Field_Draw(menufield_s * f)83 void Field_Draw( menufield_s *f )
84 {
85 int i;
86 char tempbuffer[128]="";
87
88 if ( f->generic.name )
89 Menu_DrawStringR2LDark( f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET, f->generic.y + f->generic.parent->y, f->generic.name );
90
91 strncpy( tempbuffer, f->buffer + f->visible_offset, f->visible_length );
92
93 Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y - 4, 18 );
94 Draw_Char( f->generic.x + f->generic.parent->x + 16, f->generic.y + f->generic.parent->y + 4, 24 );
95
96 Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y - 4, 20 );
97 Draw_Char( f->generic.x + f->generic.parent->x + 24 + f->visible_length * 8, f->generic.y + f->generic.parent->y + 4, 26 );
98
99 for ( i = 0; i < f->visible_length; i++ )
100 {
101 Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y - 4, 19 );
102 Draw_Char( f->generic.x + f->generic.parent->x + 24 + i * 8, f->generic.y + f->generic.parent->y + 4, 25 );
103 }
104
105 Menu_DrawString( f->generic.x + f->generic.parent->x + 24, f->generic.y + f->generic.parent->y, tempbuffer );
106
107 if ( Menu_ItemAtCursor( f->generic.parent ) == f )
108 {
109 int offset;
110
111 if ( f->visible_offset )
112 offset = f->visible_length;
113 else
114 offset = f->cursor;
115
116 if ( ( ( int ) ( Sys_Milliseconds() / 250 ) ) & 1 )
117 {
118 Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
119 f->generic.y + f->generic.parent->y,
120 11 );
121 }
122 else
123 {
124 Draw_Char( f->generic.x + f->generic.parent->x + ( offset + 2 ) * 8 + 8,
125 f->generic.y + f->generic.parent->y,
126 ' ' );
127 }
128 }
129 }
130
131 extern int keydown[];
132
Field_Key(menufield_s * f,int key)133 qboolean Field_Key( menufield_s *f, int key )
134 {
135 switch ( key )
136 {
137 case K_KP_SLASH:
138 key = '/';
139 break;
140 case K_KP_MINUS:
141 key = '-';
142 break;
143 case K_KP_PLUS:
144 key = '+';
145 break;
146 case K_KP_HOME:
147 key = '7';
148 break;
149 case K_KP_UPARROW:
150 key = '8';
151 break;
152 case K_KP_PGUP:
153 key = '9';
154 break;
155 case K_KP_LEFTARROW:
156 key = '4';
157 break;
158 case K_KP_5:
159 key = '5';
160 break;
161 case K_KP_RIGHTARROW:
162 key = '6';
163 break;
164 case K_KP_END:
165 key = '1';
166 break;
167 case K_KP_DOWNARROW:
168 key = '2';
169 break;
170 case K_KP_PGDN:
171 key = '3';
172 break;
173 case K_KP_INS:
174 key = '0';
175 break;
176 case K_KP_DEL:
177 key = '.';
178 break;
179 }
180
181 if ( key > 127 )
182 {
183 switch ( key )
184 {
185 case K_DEL:
186 default:
187 return false;
188 }
189 }
190
191 /*
192 ** support pasting from the clipboard
193 */
194 if ( ( toupper( key ) == 'V' && keydown[K_CTRL] ) ||
195 ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keydown[K_SHIFT] ) )
196 {
197 char *cbd;
198
199 if ( ( cbd = Sys_GetClipboardData() ) != 0 )
200 {
201 strtok( cbd, "\n\r\b" );
202
203 Q_strncpy (f->buffer, cbd, sizeof(f->buffer)-1);
204 f->cursor = (int)strlen( f->buffer );
205 f->visible_offset = f->cursor - f->visible_length;
206 if ( f->visible_offset < 0 )
207 f->visible_offset = 0;
208
209 free( cbd );
210 }
211 return true;
212 }
213
214 switch ( key )
215 {
216 case K_KP_LEFTARROW:
217 case K_LEFTARROW:
218 case K_BACKSPACE:
219 if ( f->cursor > 0 )
220 {
221 memmove( &f->buffer[f->cursor-1], &f->buffer[f->cursor], strlen( &f->buffer[f->cursor] ) + 1 );
222 f->cursor--;
223
224 if ( f->visible_offset )
225 {
226 f->visible_offset--;
227 }
228 }
229 break;
230
231 case K_KP_DEL:
232 case K_DEL:
233 memmove( &f->buffer[f->cursor], &f->buffer[f->cursor+1], strlen( &f->buffer[f->cursor+1] ) + 1 );
234 break;
235
236 case K_KP_ENTER:
237 case K_ENTER:
238 case K_ESCAPE:
239 case K_TAB:
240 return false;
241
242 case K_SPACE:
243 default:
244 if (( f->generic.flags & QMF_NUMBERSONLY ) && !isdigit( key ) && key != '.' )
245 return false;
246
247 if ( f->cursor < f->length )
248 {
249 f->buffer[f->cursor++] = key;
250 f->buffer[f->cursor] = 0;
251
252 if ( f->cursor > f->visible_length )
253 {
254 f->visible_offset++;
255 }
256 }
257 }
258
259 return true;
260 }
261
Menu_AddItem(menuframework_s * menu,void * item)262 void Menu_AddItem( menuframework_s *menu, void *item )
263 {
264 if ( menu->nitems == 0 )
265 menu->nslots = 0;
266
267 if ( menu->nitems < MAXMENUITEMS )
268 {
269 menu->items[menu->nitems] = item;
270 ( ( menucommon_s * ) menu->items[menu->nitems] )->parent = menu;
271 menu->nitems++;
272 }
273
274 menu->nslots = Menu_TallySlots( menu );
275 }
276
277 /*
278 ** Menu_AdjustCursor
279 **
280 ** This function takes the given menu, the direction, and attempts
281 ** to adjust the menu's cursor so that it's at the next available
282 ** slot.
283 */
Menu_AdjustCursor(menuframework_s * m,int dir)284 void Menu_AdjustCursor( menuframework_s *m, int dir )
285 {
286 menucommon_s *citem;
287
288 /*
289 ** see if it's in a valid spot
290 */
291 if ( m->cursor >= 0 && m->cursor < m->nitems )
292 {
293 if ( ( citem = Menu_ItemAtCursor( m ) ) != 0 )
294 {
295 if ( citem->type != MTYPE_SEPARATOR )
296 return;
297 }
298 }
299
300 /*
301 ** it's not in a valid spot, so crawl in the direction indicated until we
302 ** find a valid spot
303 */
304 if ( dir == 1 )
305 {
306 for (;;)
307 {
308 citem = Menu_ItemAtCursor( m );
309 if ( citem )
310 if ( citem->type != MTYPE_SEPARATOR )
311 break;
312 m->cursor += dir;
313 if ( m->cursor >= m->nitems )
314 m->cursor = 0;
315 }
316 }
317 else
318 {
319 for (;;)
320 {
321 citem = Menu_ItemAtCursor( m );
322 if ( citem )
323 if ( citem->type != MTYPE_SEPARATOR )
324 break;
325 m->cursor += dir;
326 if ( m->cursor < 0 )
327 m->cursor = m->nitems - 1;
328 }
329 }
330 }
331
Menu_Center(menuframework_s * menu)332 void Menu_Center( menuframework_s *menu )
333 {
334 int height;
335
336 height = ( ( menucommon_s * ) menu->items[menu->nitems-1])->y;
337 height += 10;
338
339 menu->y = ( VID_HEIGHT - height ) / 2;
340 }
341
Menu_Draw(menuframework_s * menu)342 void Menu_Draw( menuframework_s *menu )
343 {
344 int i;
345 menucommon_s *item;
346
347 /*
348 ** draw contents
349 */
350 for ( i = 0; i < menu->nitems; i++ )
351 {
352 switch ( ( ( menucommon_s * ) menu->items[i] )->type )
353 {
354 case MTYPE_FIELD:
355 Field_Draw( ( menufield_s * ) menu->items[i] );
356 break;
357 case MTYPE_SLIDER:
358 Slider_Draw( ( menuslider_s * ) menu->items[i] );
359 break;
360 case MTYPE_LIST:
361 MenuList_Draw( ( menulist_s * ) menu->items[i] );
362 break;
363 case MTYPE_SPINCONTROL:
364 SpinControl_Draw( ( menulist_s * ) menu->items[i] );
365 break;
366 case MTYPE_ACTION:
367 Action_Draw( ( menuaction_s * ) menu->items[i] );
368 break;
369 case MTYPE_SEPARATOR:
370 Separator_Draw( ( menuseparator_s * ) menu->items[i] );
371 break;
372 }
373 }
374
375 item = Menu_ItemAtCursor( menu );
376
377 if ( item && item->cursordraw )
378 {
379 item->cursordraw( item );
380 }
381 else if ( menu->cursordraw )
382 {
383 menu->cursordraw( menu );
384 }
385 else if ( item && item->type != MTYPE_FIELD )
386 {
387 if ( item->flags & QMF_LEFT_JUSTIFY )
388 {
389 Draw_Char( menu->x + item->x - 24 + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
390 }
391 else
392 {
393 Draw_Char( menu->x + item->cursor_offset, menu->y + item->y, 12 + ( ( int ) ( Sys_Milliseconds()/250 ) & 1 ) );
394 }
395 }
396
397 if ( item )
398 {
399 if ( item->statusbarfunc )
400 item->statusbarfunc( ( void * ) item );
401 else if ( item->statusbar )
402 Menu_DrawStatusBar( item->statusbar );
403 else
404 Menu_DrawStatusBar( menu->statusbar );
405
406 }
407 else
408 {
409 Menu_DrawStatusBar( menu->statusbar );
410 }
411 }
412
Menu_DrawStatusBar(const char * string)413 void Menu_DrawStatusBar( const char *string )
414 {
415 if ( string )
416 {
417 int l = (int)strlen( string );
418 //int maxrow = VID_HEIGHT / 8;
419 int maxcol = VID_WIDTH / 8;
420 int col = maxcol / 2 - l / 2;
421
422 Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 4 );
423 Menu_DrawString( col*8, VID_HEIGHT - 8, string );
424 }
425 else
426 {
427 Draw_Fill( 0, VID_HEIGHT-8, VID_WIDTH, 8, 0 );
428 }
429 }
430
Menu_DrawString(int x,int y,const char * string)431 void Menu_DrawString( int x, int y, const char *string )
432 {
433 uint32 i;
434
435 for ( i = 0; i < strlen( string ); i++ )
436 {
437 Draw_Char( ( x + i*8 ), y, string[i] );
438 }
439 }
440
Menu_DrawStringDark(int x,int y,const char * string)441 void Menu_DrawStringDark( int x, int y, const char *string )
442 {
443 uint32 i;
444
445 for ( i = 0; i < strlen( string ); i++ )
446 {
447 Draw_Char( ( x + i*8 ), y, string[i] + 128 );
448 }
449 }
450
Menu_DrawStringR2L(int x,int y,const char * string)451 void Menu_DrawStringR2L( int x, int y, const char *string )
452 {
453 uint32 i;
454
455 for ( i = 0; i < strlen( string ); i++ )
456 {
457 Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1] );
458 }
459 }
460
Menu_DrawStringR2LDark(int x,int y,const char * string)461 void Menu_DrawStringR2LDark( int x, int y, const char *string )
462 {
463 uint32 i;
464
465 for ( i = 0; i < strlen( string ); i++ )
466 {
467 Draw_Char( ( x - i*8 ), y, string[strlen(string)-i-1]+128 );
468 }
469 }
470
Menu_ItemAtCursor(menuframework_s * m)471 void *Menu_ItemAtCursor( menuframework_s *m )
472 {
473 if ( m->cursor < 0 || m->cursor >= m->nitems )
474 return 0;
475
476 return m->items[m->cursor];
477 }
478
Menu_SelectItem(menuframework_s * s)479 qboolean Menu_SelectItem( menuframework_s *s )
480 {
481 menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
482
483 if ( item )
484 {
485 switch ( item->type )
486 {
487 case MTYPE_FIELD:
488 return Field_DoEnter( ( menufield_s * ) item ) ;
489 case MTYPE_ACTION:
490 Action_DoEnter( ( menuaction_s * ) item );
491 return true;
492 case MTYPE_LIST:
493 // Menulist_DoEnter( ( menulist_s * ) item );
494 return false;
495 case MTYPE_SPINCONTROL:
496 // SpinControl_DoEnter( ( menulist_s * ) item );
497 return false;
498 }
499 }
500 return false;
501 }
502
Menu_SetStatusBar(menuframework_s * m,const char * string)503 void Menu_SetStatusBar( menuframework_s *m, const char *string )
504 {
505 m->statusbar = string;
506 }
507
Menu_SlideItem(menuframework_s * s,int dir)508 void Menu_SlideItem( menuframework_s *s, int dir )
509 {
510 menucommon_s *item = ( menucommon_s * ) Menu_ItemAtCursor( s );
511
512 if ( item )
513 {
514 switch ( item->type )
515 {
516 case MTYPE_SLIDER:
517 Slider_DoSlide( ( menuslider_s * ) item, dir );
518 break;
519 case MTYPE_SPINCONTROL:
520 SpinControl_DoSlide( ( menulist_s * ) item, dir );
521 break;
522 }
523 }
524 }
525
Menu_TallySlots(menuframework_s * menu)526 int Menu_TallySlots( menuframework_s *menu )
527 {
528 int i;
529 int total = 0;
530
531 for ( i = 0; i < menu->nitems; i++ )
532 {
533 if ( ( ( menucommon_s * ) menu->items[i] )->type == MTYPE_LIST )
534 {
535 int nitems = 0;
536 const char **n = ( ( menulist_s * ) menu->items[i] )->itemnames;
537
538 while (*n)
539 nitems++, n++;
540
541 total += nitems;
542 }
543 else
544 {
545 total++;
546 }
547 }
548
549 return total;
550 }
551
552 /*void Menulist_DoEnter( menulist_s *l )
553 {
554 int start;
555
556 start = l->generic.y / 10 + 1;
557
558 l->curvalue = l->generic.parent->cursor - start;
559
560 if ( l->generic.callback )
561 l->generic.callback( l );
562 }*/
563
MenuList_Draw(menulist_s * l)564 void MenuList_Draw( menulist_s *l )
565 {
566 const char **n;
567 int y = 0;
568
569 Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y, l->generic.name );
570
571 n = l->itemnames;
572
573 Draw_Fill( l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue*10 + 10, 128, 10, 16 );
574 while ( *n )
575 {
576 Menu_DrawStringR2LDark( l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET, l->generic.y + l->generic.parent->y + y + 10, *n );
577
578 n++;
579 y += 10;
580 }
581 }
582
Separator_Draw(menuseparator_s * s)583 void Separator_Draw( menuseparator_s *s )
584 {
585 if ( s->generic.name )
586 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name );
587 }
588
Slider_DoSlide(menuslider_s * s,int dir)589 void Slider_DoSlide( menuslider_s *s, int dir )
590 {
591 s->curvalue += dir;
592
593 if ( s->curvalue > s->maxvalue )
594 s->curvalue = s->maxvalue;
595 else if ( s->curvalue < s->minvalue )
596 s->curvalue = s->minvalue;
597
598 if ( s->generic.callback )
599 s->generic.callback( s );
600 }
601
602 #define SLIDER_RANGE 10
603
Slider_Draw(menuslider_s * s)604 void Slider_Draw( menuslider_s *s )
605 {
606 int i;
607
608 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
609 s->generic.y + s->generic.parent->y,
610 s->generic.name );
611
612 s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
613
614 if (FLOAT_LT_ZERO(s->range))
615 s->range = 0;
616 if ( s->range > 1)
617 s->range = 1;
618 Draw_Char( s->generic.x + s->generic.parent->x + RCOLUMN_OFFSET, s->generic.y + s->generic.parent->y, 128);
619 for ( i = 0; i < SLIDER_RANGE; i++ )
620 Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 129);
621 Draw_Char( RCOLUMN_OFFSET + s->generic.x + i*8 + s->generic.parent->x + 8, s->generic.y + s->generic.parent->y, 130);
622 Draw_Char( ( int ) ( 8 + RCOLUMN_OFFSET + s->generic.parent->x + s->generic.x + (SLIDER_RANGE-1)*8 * s->range ), s->generic.y + s->generic.parent->y, 131);
623 }
624
625 /*void SpinControl_DoEnter( menulist_s *s )
626 {
627 s->curvalue++;
628 if ( s->itemnames[s->curvalue] == 0 )
629 s->curvalue = 0;
630
631 if ( s->generic.callback )
632 s->generic.callback( s );
633 }*/
634
SpinControl_DoSlide(menulist_s * s,int dir)635 void SpinControl_DoSlide( menulist_s *s, int dir )
636 {
637 s->curvalue += dir;
638
639 if ( s->curvalue < 0 )
640 s->curvalue = 0;
641 else if ( s->itemnames[s->curvalue] == 0 )
642 s->curvalue--;
643
644 if ( s->generic.callback )
645 s->generic.callback( s );
646 }
647
SpinControl_Draw(menulist_s * s)648 void SpinControl_Draw( menulist_s *s )
649 {
650 char buffer[100];
651
652 if ( s->generic.name )
653 {
654 Menu_DrawStringR2LDark( s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET,
655 s->generic.y + s->generic.parent->y,
656 s->generic.name );
657 }
658
659 if (!s->itemnames[s->curvalue])
660 Com_Error (ERR_DROP, "SpinControl_Draw: %s: NULL current value %d", s->generic.name, s->curvalue);
661
662 if ( !strchr( s->itemnames[s->curvalue], '\n' ) )
663 {
664 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue] );
665 }
666 else
667 {
668 strcpy( buffer, s->itemnames[s->curvalue] );
669 *strchr( buffer, '\n' ) = 0;
670 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer );
671 strcpy( buffer, strchr( s->itemnames[s->curvalue], '\n' ) + 1 );
672 Menu_DrawString( RCOLUMN_OFFSET + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer );
673 }
674 }
675
676