ui_mfield.c (8901B)
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 Foobar; 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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