1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 #include "ui_local.h"
24 
25 /*
26 ===================
27 MField_Draw
28 
29 Handles horizontal scrolling and cursor blinking
30 x, y, are in pixels
31 ===================
32 */
MField_Draw(mfield_t * edit,int x,int y,int style,vec4_t color)33 void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ) {
34 	int		len;
35 	int		charw;
36 	int		drawLen;
37 	int		prestep;
38 	int		cursorChar;
39 	char	str[MAX_STRING_CHARS];
40 
41 	drawLen = edit->widthInChars;
42 	len     = strlen( edit->buffer ) + 1;
43 
44 	// guarantee that cursor will be visible
45 	if ( len <= drawLen ) {
46 		prestep = 0;
47 	} else {
48 		if ( edit->scroll + drawLen > len ) {
49 			edit->scroll = len - drawLen;
50 			if ( edit->scroll < 0 ) {
51 				edit->scroll = 0;
52 			}
53 		}
54 		prestep = edit->scroll;
55 	}
56 
57 	if ( prestep + drawLen > len ) {
58 		drawLen = len - prestep;
59 	}
60 
61 	// extract <drawLen> characters from the field at <prestep>
62 	if ( drawLen >= MAX_STRING_CHARS ) {
63 		trap_Error( "drawLen >= MAX_STRING_CHARS" );
64 	}
65 	memcpy( str, edit->buffer + prestep, drawLen );
66 	str[ drawLen ] = 0;
67 
68 	UI_DrawString( x, y, str, style, color );
69 
70 	// draw the cursor
71 	if (!(style & UI_PULSE)) {
72 		return;
73 	}
74 
75 	if ( trap_Key_GetOverstrikeMode() ) {
76 		cursorChar = 11;
77 	} else {
78 		cursorChar = 10;
79 	}
80 
81 	style &= ~UI_PULSE;
82 	style |= UI_BLINK;
83 
84 	if (style & UI_SMALLFONT)
85 	{
86 		charw =	SMALLCHAR_WIDTH;
87 	}
88 	else if (style & UI_GIANTFONT)
89 	{
90 		charw =	GIANTCHAR_WIDTH;
91 	}
92 	else
93 	{
94 		charw =	BIGCHAR_WIDTH;
95 	}
96 
97 	if (style & UI_CENTER)
98 	{
99 		len = strlen(str);
100 		x = x - len*charw/2;
101 	}
102 	else if (style & UI_RIGHT)
103 	{
104 		len = strlen(str);
105 		x = x - len*charw;
106 	}
107 
108 	UI_DrawChar( x + ( edit->cursor - prestep ) * charw, y, cursorChar, style & ~(UI_CENTER|UI_RIGHT), color );
109 }
110 
111 /*
112 ================
113 MField_Paste
114 ================
115 */
MField_Paste(mfield_t * edit)116 void MField_Paste( mfield_t *edit ) {
117 	char	pasteBuffer[64];
118 	int		pasteLen, i;
119 
120 	trap_GetClipboardData( pasteBuffer, 64 );
121 
122 	// send as if typed, so insert / overstrike works properly
123 	pasteLen = strlen( pasteBuffer );
124 	for ( i = 0 ; i < pasteLen ; i++ ) {
125 		MField_CharEvent( edit, pasteBuffer[i] );
126 	}
127 }
128 
129 /*
130 =================
131 MField_KeyDownEvent
132 
133 Performs the basic line editing functions for the console,
134 in-game talk, and menu fields
135 
136 Key events are used for non-printable characters, others are gotten from char events.
137 =================
138 */
MField_KeyDownEvent(mfield_t * edit,int key)139 void MField_KeyDownEvent( mfield_t *edit, int key ) {
140 	int		len;
141 
142 	// shift-insert is paste
143 	if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && trap_Key_IsDown( K_SHIFT ) ) {
144 		MField_Paste( edit );
145 		return;
146 	}
147 
148 	len = strlen( edit->buffer );
149 
150 	if ( key == K_DEL || key == K_KP_DEL ) {
151 		if ( edit->cursor < len ) {
152 			memmove( edit->buffer + edit->cursor,
153 				edit->buffer + edit->cursor + 1, len - edit->cursor );
154 		}
155 		return;
156 	}
157 
158 	if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW )
159 	{
160 		if ( edit->cursor < len ) {
161 			edit->cursor++;
162 		}
163 		if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
164 		{
165 			edit->scroll++;
166 		}
167 		return;
168 	}
169 
170 	if ( key == K_LEFTARROW || key == K_KP_LEFTARROW )
171 	{
172 		if ( edit->cursor > 0 ) {
173 			edit->cursor--;
174 		}
175 		if ( edit->cursor < edit->scroll )
176 		{
177 			edit->scroll--;
178 		}
179 		return;
180 	}
181 
182 	if ( key == K_HOME || key == K_KP_HOME || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) {
183 		edit->cursor = 0;
184 		edit->scroll = 0;
185 		return;
186 	}
187 
188 	if ( key == K_END || key == K_KP_END || ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) {
189 		edit->cursor = len;
190 		edit->scroll = len - edit->widthInChars + 1;
191 		if (edit->scroll < 0)
192 			edit->scroll = 0;
193 		return;
194 	}
195 
196 	if ( key == K_INS || key == K_KP_INS ) {
197 		trap_Key_SetOverstrikeMode( !trap_Key_GetOverstrikeMode() );
198 		return;
199 	}
200 }
201 
202 /*
203 ==================
204 MField_CharEvent
205 ==================
206 */
MField_CharEvent(mfield_t * edit,int ch)207 void MField_CharEvent( mfield_t *edit, int ch ) {
208 	int		len;
209 
210 	if ( ch == 'v' - 'a' + 1 ) {	// ctrl-v is paste
211 		MField_Paste( edit );
212 		return;
213 	}
214 
215 	if ( ch == 'c' - 'a' + 1 ) {	// ctrl-c clears the field
216 		MField_Clear( edit );
217 		return;
218 	}
219 
220 	len = strlen( edit->buffer );
221 
222 	if ( ch == 'h' - 'a' + 1 )	{	// ctrl-h is backspace
223 		if ( edit->cursor > 0 ) {
224 			memmove( edit->buffer + edit->cursor - 1,
225 				edit->buffer + edit->cursor, len + 1 - edit->cursor );
226 			edit->cursor--;
227 			if ( edit->cursor < edit->scroll )
228 			{
229 				edit->scroll--;
230 			}
231 		}
232 		return;
233 	}
234 
235 	if ( ch == 'a' - 'a' + 1 ) {	// ctrl-a is home
236 		edit->cursor = 0;
237 		edit->scroll = 0;
238 		return;
239 	}
240 
241 	if ( ch == 'e' - 'a' + 1 ) {	// ctrl-e is end
242 		edit->cursor = len;
243 		edit->scroll = edit->cursor - edit->widthInChars + 1;
244 		if (edit->scroll < 0)
245 			edit->scroll = 0;
246 		return;
247 	}
248 
249 	//
250 	// ignore any other non printable chars
251 	//
252 	if ( ch < 32 ) {
253 		return;
254 	}
255 
256 	if ( !trap_Key_GetOverstrikeMode() ) {
257 		if ((edit->cursor == MAX_EDIT_LINE - 1) || (edit->maxchars && edit->cursor >= edit->maxchars))
258 			return;
259 	} else {
260 		// insert mode
261 		if (( len == MAX_EDIT_LINE - 1 ) || (edit->maxchars && len >= edit->maxchars))
262 			return;
263 		memmove( edit->buffer + edit->cursor + 1, edit->buffer + edit->cursor, len + 1 - edit->cursor );
264 	}
265 
266 	edit->buffer[edit->cursor] = ch;
267 	if (!edit->maxchars || edit->cursor < edit->maxchars-1)
268 		edit->cursor++;
269 
270 	if ( edit->cursor >= edit->widthInChars )
271 	{
272 		edit->scroll++;
273 	}
274 
275 	if ( edit->cursor == len + 1) {
276 		edit->buffer[edit->cursor] = 0;
277 	}
278 }
279 
280 /*
281 ==================
282 MField_Clear
283 ==================
284 */
MField_Clear(mfield_t * edit)285 void MField_Clear( mfield_t *edit ) {
286 	edit->buffer[0] = 0;
287 	edit->cursor = 0;
288 	edit->scroll = 0;
289 }
290 
291 /*
292 ==================
293 MenuField_Init
294 ==================
295 */
MenuField_Init(menufield_s * m)296 void MenuField_Init( menufield_s* m ) {
297 	int	l;
298 	int	w;
299 	int	h;
300 
301 	MField_Clear( &m->field );
302 
303 	if (m->generic.flags & QMF_SMALLFONT)
304 	{
305 		w = SMALLCHAR_WIDTH;
306 		h = SMALLCHAR_HEIGHT;
307 	}
308 	else
309 	{
310 		w = BIGCHAR_WIDTH;
311 		h = BIGCHAR_HEIGHT;
312 	}
313 
314 	if (m->generic.name) {
315 		l = (strlen( m->generic.name )+1) * w;
316 	}
317 	else {
318 		l = 0;
319 	}
320 
321 	m->generic.left   = m->generic.x - l;
322 	m->generic.top    = m->generic.y;
323 	m->generic.right  = m->generic.x + w + m->field.widthInChars*w;
324 	m->generic.bottom = m->generic.y + h;
325 }
326 
327 /*
328 ==================
329 MenuField_Draw
330 ==================
331 */
MenuField_Draw(menufield_s * f)332 void MenuField_Draw( menufield_s *f )
333 {
334 	int		x;
335 	int		y;
336 	int		w;
337 	int		h;
338 	int		style;
339 	qboolean focus;
340 	float	*color;
341 
342 	x =	f->generic.x;
343 	y =	f->generic.y;
344 
345 	if (f->generic.flags & QMF_SMALLFONT)
346 	{
347 		w = SMALLCHAR_WIDTH;
348 		h = SMALLCHAR_HEIGHT;
349 		style = UI_SMALLFONT;
350 	}
351 	else
352 	{
353 		w = BIGCHAR_WIDTH;
354 		h = BIGCHAR_HEIGHT;
355 		style = UI_BIGFONT;
356 	}
357 
358 	if (Menu_ItemAtCursor( f->generic.parent ) == f) {
359 		focus = qtrue;
360 		style |= UI_PULSE;
361 	}
362 	else {
363 		focus = qfalse;
364 	}
365 
366 	if (f->generic.flags & QMF_GRAYED)
367 		color = text_color_disabled;
368 	else if (focus)
369 		color = text_color_highlight;
370 	else
371 		color = text_color_normal;
372 
373 	if ( focus )
374 	{
375 		// draw cursor
376 		UI_FillRect( f->generic.left, f->generic.top, f->generic.right-f->generic.left+1, f->generic.bottom-f->generic.top+1, listbar_color );
377 		UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|style, color);
378 	}
379 
380 	if ( f->generic.name ) {
381 		UI_DrawString( x - w, y, f->generic.name, style|UI_RIGHT, color );
382 	}
383 
384 	MField_Draw( &f->field, x + w, y, style, color );
385 }
386 
387 /*
388 ==================
389 MenuField_Key
390 ==================
391 */
MenuField_Key(menufield_s * m,int * key)392 sfxHandle_t MenuField_Key( menufield_s* m, int* key )
393 {
394 	int keycode;
395 
396 	keycode = *key;
397 
398 	switch ( keycode )
399 	{
400 		case K_KP_ENTER:
401 		case K_ENTER:
402 		case K_JOY1:
403 		case K_JOY2:
404 		case K_JOY3:
405 		case K_JOY4:
406 			// have enter go to next cursor point
407 			*key = K_TAB;
408 			break;
409 
410 		case K_TAB:
411 		case K_KP_DOWNARROW:
412 		case K_DOWNARROW:
413 		case K_KP_UPARROW:
414 		case K_UPARROW:
415 			break;
416 
417 		default:
418 			if ( keycode & K_CHAR_FLAG )
419 			{
420 				keycode &= ~K_CHAR_FLAG;
421 
422 				if ((m->generic.flags & QMF_UPPERCASE) && Q_islower( keycode ))
423 					keycode -= 'a' - 'A';
424 				else if ((m->generic.flags & QMF_LOWERCASE) && Q_isupper( keycode ))
425 					keycode -= 'A' - 'a';
426 				else if ((m->generic.flags & QMF_NUMBERSONLY) && Q_isalpha( keycode ))
427 					return (menu_buzz_sound);
428 
429 				MField_CharEvent( &m->field, keycode);
430 			}
431 			else
432 				MField_KeyDownEvent( &m->field, keycode );
433 			break;
434 	}
435 
436 	return (0);
437 }
438 
439 
440