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