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