DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

EditField.cpp (14080B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #include "../idlib/precompiled.h"
     30 #pragma hdrstop
     31 
     32 static autoComplete_t	globalAutoComplete;
     33 
     34 /*
     35 ===============
     36 FindMatches
     37 ===============
     38 */
     39 static void FindMatches( const char *s ) {
     40 	int		i;
     41 
     42 	if ( idStr::Icmpn( s, globalAutoComplete.completionString, strlen( globalAutoComplete.completionString ) ) != 0 ) {
     43 		return;
     44 	}
     45 	globalAutoComplete.matchCount++;
     46 	if ( globalAutoComplete.matchCount == 1 ) {
     47 		idStr::Copynz( globalAutoComplete.currentMatch, s, sizeof( globalAutoComplete.currentMatch ) );
     48 		return;
     49 	}
     50 
     51 	// cut currentMatch to the amount common with s
     52 	for ( i = 0; s[i]; i++ ) {
     53 		if ( tolower( globalAutoComplete.currentMatch[i] ) != tolower( s[i] ) ) {
     54 			globalAutoComplete.currentMatch[i] = 0;
     55 			break;
     56 		}
     57 	}
     58 	globalAutoComplete.currentMatch[i] = 0;
     59 }
     60 
     61 /*
     62 ===============
     63 FindIndexMatch
     64 ===============
     65 */
     66 static void FindIndexMatch( const char *s ) {
     67 
     68 	if ( idStr::Icmpn( s, globalAutoComplete.completionString, strlen( globalAutoComplete.completionString ) ) != 0 ) {
     69 		return;
     70 	}
     71 
     72 	if( globalAutoComplete.findMatchIndex == globalAutoComplete.matchIndex ) {
     73 		idStr::Copynz( globalAutoComplete.currentMatch, s, sizeof( globalAutoComplete.currentMatch ) );
     74 	}
     75 
     76 	globalAutoComplete.findMatchIndex++;
     77 }
     78 
     79 /*
     80 ===============
     81 PrintMatches
     82 ===============
     83 */
     84 static void PrintMatches( const char *s ) {
     85 	if ( idStr::Icmpn( s, globalAutoComplete.currentMatch, strlen( globalAutoComplete.currentMatch ) ) == 0 ) {
     86 		common->Printf( "    %s\n", s );
     87 	}
     88 }
     89 
     90 /*
     91 ===============
     92 PrintCvarMatches
     93 ===============
     94 */
     95 static void PrintCvarMatches( const char *s ) {
     96 	if ( idStr::Icmpn( s, globalAutoComplete.currentMatch, strlen( globalAutoComplete.currentMatch ) ) == 0 ) {
     97 		common->Printf( "    %s" S_COLOR_WHITE " = \"%s\"\n", s, cvarSystem->GetCVarString( s ) );
     98 	}
     99 }
    100 
    101 /*
    102 ===============
    103 idEditField::idEditField
    104 ===============
    105 */
    106 idEditField::idEditField() {
    107 	widthInChars = 0;
    108 	Clear();
    109 }
    110 
    111 /*
    112 ===============
    113 idEditField::~idEditField
    114 ===============
    115 */
    116 idEditField::~idEditField() {
    117 }
    118 
    119 /*
    120 ===============
    121 idEditField::Clear
    122 ===============
    123 */
    124 void idEditField::Clear() {
    125 	buffer[0] = 0;
    126 	cursor = 0;
    127 	scroll = 0;
    128 	autoComplete.length = 0;
    129 	autoComplete.valid = false;
    130 }
    131 
    132 /*
    133 ===============
    134 idEditField::SetWidthInChars
    135 ===============
    136 */
    137 void idEditField::SetWidthInChars( int w ) {
    138 	assert( w <= MAX_EDIT_LINE );
    139 	widthInChars = w;
    140 }
    141 
    142 /*
    143 ===============
    144 idEditField::SetCursor
    145 ===============
    146 */
    147 void idEditField::SetCursor( int c ) {
    148 	assert( c <= MAX_EDIT_LINE );
    149 	cursor = c;
    150 }
    151 
    152 /*
    153 ===============
    154 idEditField::GetCursor
    155 ===============
    156 */
    157 int idEditField::GetCursor() const {
    158 	return cursor;
    159 }
    160 
    161 /*
    162 ===============
    163 idEditField::ClearAutoComplete
    164 ===============
    165 */
    166 void idEditField::ClearAutoComplete() {
    167 	if ( autoComplete.length > 0 && autoComplete.length <= (int) strlen( buffer ) ) {
    168 		buffer[autoComplete.length] = '\0';
    169 		if ( cursor > autoComplete.length ) {
    170 			cursor = autoComplete.length;
    171 		}
    172 	}
    173 	autoComplete.length = 0;
    174 	autoComplete.valid = false;
    175 }
    176 
    177 /*
    178 ===============
    179 idEditField::GetAutoCompleteLength
    180 ===============
    181 */
    182 int idEditField::GetAutoCompleteLength() const {
    183 	return autoComplete.length;
    184 }
    185 
    186 /*
    187 ===============
    188 idEditField::AutoComplete
    189 ===============
    190 */
    191 void idEditField::AutoComplete() {
    192 	char completionArgString[MAX_EDIT_LINE];
    193 	idCmdArgs args;
    194 
    195 	if ( !autoComplete.valid ) {
    196 		args.TokenizeString( buffer, false );
    197 		idStr::Copynz( autoComplete.completionString, args.Argv( 0 ), sizeof( autoComplete.completionString ) );
    198 		idStr::Copynz( completionArgString, args.Args(), sizeof( completionArgString ) );
    199 		autoComplete.matchCount = 0;
    200 		autoComplete.matchIndex = 0;
    201 		autoComplete.currentMatch[0] = 0;
    202 
    203 		if ( strlen( autoComplete.completionString ) == 0 ) {
    204 			return;
    205 		}
    206 
    207 		globalAutoComplete = autoComplete;
    208 
    209 		cmdSystem->CommandCompletion( FindMatches );
    210 		cvarSystem->CommandCompletion( FindMatches );
    211 
    212 		autoComplete = globalAutoComplete;
    213 
    214 		if ( autoComplete.matchCount == 0 ) {
    215 			return;	// no matches
    216 		}
    217 
    218 		// when there's only one match or there's an argument
    219 		if ( autoComplete.matchCount == 1 || completionArgString[0] != '\0' ) {
    220 
    221 			/// try completing arguments
    222 			idStr::Append( autoComplete.completionString, sizeof( autoComplete.completionString ), " " );
    223 			idStr::Append( autoComplete.completionString, sizeof( autoComplete.completionString ), completionArgString );
    224 			autoComplete.matchCount = 0;
    225 
    226 			globalAutoComplete = autoComplete;
    227 
    228 			cmdSystem->ArgCompletion( autoComplete.completionString, FindMatches );
    229 			cvarSystem->ArgCompletion( autoComplete.completionString, FindMatches );
    230 
    231 			autoComplete = globalAutoComplete;
    232 
    233 			idStr::snPrintf( buffer, sizeof( buffer ), "%s", autoComplete.currentMatch );
    234 
    235 			if ( autoComplete.matchCount == 0 ) {
    236 				// no argument matches
    237 				idStr::Append( buffer, sizeof( buffer ), " " );
    238 				idStr::Append( buffer, sizeof( buffer ), completionArgString );
    239 				SetCursor( strlen( buffer ) );
    240 				return;
    241 			}
    242 		} else {
    243 
    244 			// multiple matches, complete to shortest
    245 			idStr::snPrintf( buffer, sizeof( buffer ), "%s", autoComplete.currentMatch );
    246 			if ( strlen( completionArgString ) ) {
    247 				idStr::Append( buffer, sizeof( buffer ), " " );
    248 				idStr::Append( buffer, sizeof( buffer ), completionArgString );
    249 			}
    250 		}
    251 
    252 		autoComplete.length = strlen( buffer );
    253 		autoComplete.valid = ( autoComplete.matchCount != 1 );
    254 		SetCursor( autoComplete.length );
    255 
    256 		common->Printf( "]%s\n", buffer );
    257 
    258 		// run through again, printing matches
    259 		globalAutoComplete = autoComplete;
    260 
    261 		cmdSystem->CommandCompletion( PrintMatches );
    262 		cmdSystem->ArgCompletion( autoComplete.completionString, PrintMatches );
    263 		cvarSystem->CommandCompletion( PrintCvarMatches );
    264 		cvarSystem->ArgCompletion( autoComplete.completionString, PrintMatches );
    265 
    266 	} else if ( autoComplete.matchCount != 1 ) {
    267 
    268 		// get the next match and show instead
    269 		autoComplete.matchIndex++;
    270 		if ( autoComplete.matchIndex == autoComplete.matchCount ) {
    271 			autoComplete.matchIndex = 0;
    272 		}
    273 		autoComplete.findMatchIndex = 0;
    274 
    275 		globalAutoComplete = autoComplete;
    276 
    277 		cmdSystem->CommandCompletion( FindIndexMatch );
    278 		cmdSystem->ArgCompletion( autoComplete.completionString, FindIndexMatch );
    279 		cvarSystem->CommandCompletion( FindIndexMatch );
    280 		cvarSystem->ArgCompletion( autoComplete.completionString, FindIndexMatch );
    281 
    282 		autoComplete = globalAutoComplete;
    283 
    284 		// and print it
    285 		idStr::snPrintf( buffer, sizeof( buffer ), autoComplete.currentMatch );
    286 		if ( autoComplete.length > (int)strlen( buffer ) ) {
    287 			autoComplete.length = strlen( buffer );
    288 		}
    289 		SetCursor( autoComplete.length );
    290 	}
    291 }
    292 
    293 /*
    294 ===============
    295 idEditField::CharEvent
    296 ===============
    297 */
    298 void idEditField::CharEvent( int ch ) {
    299 	int		len;
    300 
    301 	if ( ch == 'v' - 'a' + 1 ) {	// ctrl-v is paste
    302 		Paste();
    303 		return;
    304 	}
    305 
    306 	if ( ch == 'c' - 'a' + 1 ) {	// ctrl-c clears the field
    307 		Clear();
    308 		return;
    309 	}
    310 
    311 	len = strlen( buffer );
    312 
    313 	if ( ch == 'h' - 'a' + 1 || ch == K_BACKSPACE ) {	// ctrl-h is backspace
    314 		if ( cursor > 0 ) {
    315 			memmove( buffer + cursor - 1, buffer + cursor, len + 1 - cursor );
    316 			cursor--;
    317 			if ( cursor < scroll ) {
    318 				scroll--;
    319 			}
    320 		}
    321 		return;
    322 	}
    323 
    324 	if ( ch == 'a' - 'a' + 1 ) {	// ctrl-a is home
    325 		cursor = 0;
    326 		scroll = 0;
    327 		return;
    328 	}
    329 
    330 	if ( ch == 'e' - 'a' + 1 ) {	// ctrl-e is end
    331 		cursor = len;
    332 		scroll = cursor - widthInChars;
    333 		return;
    334 	}
    335 
    336 	//
    337 	// ignore any other non printable chars
    338 	//
    339 	if ( ch < 32 ) {
    340 		return;
    341 	}
    342 
    343 	if ( idKeyInput::GetOverstrikeMode() ) {	
    344 		if ( cursor == MAX_EDIT_LINE - 1 ) {
    345 			return;
    346 		}
    347 		buffer[cursor] = ch;
    348 		cursor++;
    349 	} else {	// insert mode
    350 		if ( len == MAX_EDIT_LINE - 1 ) {
    351 			return; // all full
    352 		}
    353 		memmove( buffer + cursor + 1, buffer + cursor, len + 1 - cursor );
    354 		buffer[cursor] = ch;
    355 		cursor++;
    356 	}
    357 
    358 
    359 	if ( cursor >= widthInChars ) {
    360 		scroll++;
    361 	}
    362 
    363 	if ( cursor == len + 1 ) {
    364 		buffer[cursor] = 0;
    365 	}
    366 }
    367 
    368 /*
    369 ===============
    370 idEditField::KeyDownEvent
    371 ===============
    372 */
    373 void idEditField::KeyDownEvent( int key ) {
    374 	int		len;
    375 
    376 	// shift-insert is paste
    377 	if ( ( ( key == K_INS ) || ( key == K_KP_0 ) ) && ( idKeyInput::IsDown( K_LSHIFT ) || idKeyInput::IsDown( K_RSHIFT ) ) ) {
    378 		ClearAutoComplete();
    379 		Paste();
    380 		return;
    381 	}
    382 
    383 	len = strlen( buffer );
    384 
    385 	if ( key == K_DEL ) {
    386 		if ( autoComplete.length ) {
    387 			ClearAutoComplete();
    388 		} else if ( cursor < len ) {
    389 			memmove( buffer + cursor, buffer + cursor + 1, len - cursor );
    390 		}
    391 		return;
    392 	}
    393 
    394 	if ( key == K_RIGHTARROW ) {
    395 		if ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) {
    396 			// skip to next word
    397 			while( ( cursor < len ) && ( buffer[ cursor ] != ' ' ) ) {
    398 				cursor++;
    399 			}
    400 
    401 			while( ( cursor < len ) && ( buffer[ cursor ] == ' ' ) ) {
    402 				cursor++;
    403 			}
    404 		} else {
    405 			cursor++;
    406 		}
    407 
    408 		if ( cursor > len ) {
    409 			cursor = len;
    410 		}
    411 
    412 		if ( cursor >= scroll + widthInChars ) {
    413 			scroll = cursor - widthInChars + 1;
    414 		}
    415 
    416 		if ( autoComplete.length > 0 ) {
    417 			autoComplete.length = cursor;
    418 		}
    419 		return;
    420 	}
    421 
    422 	if ( key == K_LEFTARROW ) {
    423 		if ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) {
    424 			// skip to previous word
    425 			while( ( cursor > 0 ) && ( buffer[ cursor - 1 ] == ' ' ) ) {
    426 				cursor--;
    427 			}
    428 
    429 			while( ( cursor > 0 ) && ( buffer[ cursor - 1 ] != ' ' ) ) {
    430 				cursor--;
    431 			}
    432 		} else {
    433 			cursor--;
    434 		}
    435 
    436 		if ( cursor < 0 ) {
    437 			cursor = 0;
    438 		}
    439 		if ( cursor < scroll ) {
    440 			scroll = cursor;
    441 		}
    442 
    443 		if ( autoComplete.length ) {
    444 			autoComplete.length = cursor;
    445 		}
    446 		return;
    447 	}
    448 
    449 	if ( key == K_HOME || ( key == K_A && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) {
    450 		cursor = 0;
    451 		scroll = 0;
    452 		if ( autoComplete.length ) {
    453 			autoComplete.length = cursor;
    454 			autoComplete.valid = false;
    455 		}
    456 		return;
    457 	}
    458 
    459 	if ( key == K_END || ( key == K_E && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) {
    460 		cursor = len;
    461 		if ( cursor >= scroll + widthInChars ) {
    462 			scroll = cursor - widthInChars + 1;
    463 		}
    464 		if ( autoComplete.length ) {
    465 			autoComplete.length = cursor;
    466 			autoComplete.valid = false;
    467 		}
    468 		return;
    469 	}
    470 
    471 	if ( key == K_INS ) {
    472 		idKeyInput::SetOverstrikeMode( !idKeyInput::GetOverstrikeMode() );
    473 		return;
    474 	}
    475 
    476 	// clear autocompletion buffer on normal key input
    477 	if ( key != K_CAPSLOCK && key != K_LALT && key != K_LCTRL && key != K_LSHIFT && key != K_RALT && key != K_RCTRL && key != K_RSHIFT ) {
    478 		ClearAutoComplete();
    479 	}
    480 }
    481 
    482 /*
    483 ===============
    484 idEditField::Paste
    485 ===============
    486 */
    487 void idEditField::Paste() {
    488 	char	*cbd;
    489 	int		pasteLen, i;
    490 
    491 	cbd = Sys_GetClipboardData();
    492 
    493 	if ( !cbd ) {
    494 		return;
    495 	}
    496 
    497 	// send as if typed, so insert / overstrike works properly
    498 	pasteLen = strlen( cbd );
    499 	for ( i = 0; i < pasteLen; i++ ) {
    500 		CharEvent( cbd[i] );
    501 	}
    502 
    503 	Mem_Free( cbd );
    504 }
    505 
    506 /*
    507 ===============
    508 idEditField::GetBuffer
    509 ===============
    510 */
    511 char *idEditField::GetBuffer() {
    512 	return buffer;
    513 }
    514 
    515 /*
    516 ===============
    517 idEditField::SetBuffer
    518 ===============
    519 */
    520 void idEditField::SetBuffer( const char *buf ) {
    521 	Clear();
    522 	idStr::Copynz( buffer, buf, sizeof( buffer ) );
    523 	SetCursor( strlen( buffer ) );
    524 }
    525 
    526 /*
    527 ===============
    528 idEditField::Draw
    529 ===============
    530 */
    531 void idEditField::Draw( int x, int y, int width, bool showCursor ) {
    532 	int		len;
    533 	int		drawLen;
    534 	int		prestep;
    535 	int		cursorChar;
    536 	char	str[MAX_EDIT_LINE];
    537 	int		size;
    538 
    539 	size = SMALLCHAR_WIDTH;
    540 
    541 	drawLen = widthInChars;
    542 	len = strlen( buffer ) + 1;
    543 
    544 	// guarantee that cursor will be visible
    545 	if ( len <= drawLen ) {
    546 		prestep = 0;
    547 	} else {
    548 		if ( scroll + drawLen > len ) {
    549 			scroll = len - drawLen;
    550 			if ( scroll < 0 ) {
    551 				scroll = 0;
    552 			}
    553 		}
    554 		prestep = scroll;
    555 
    556 		// Skip color code
    557 		if ( idStr::IsColor( buffer + prestep ) ) { 
    558 			prestep += 2;
    559 		}
    560 		if ( prestep > 0 && idStr::IsColor( buffer + prestep - 1 ) ) {
    561 			prestep++;
    562 		}
    563 	}
    564 
    565 	if ( prestep + drawLen > len ) {
    566 		drawLen = len - prestep;
    567 	}
    568 
    569 	// extract <drawLen> characters from the field at <prestep>
    570 	if ( drawLen >= MAX_EDIT_LINE ) {
    571 		common->Error( "drawLen >= MAX_EDIT_LINE" );
    572 	}
    573 
    574 	memcpy( str, buffer + prestep, drawLen );
    575 	str[ drawLen ] = 0;
    576 
    577 	// draw it
    578 	renderSystem->DrawSmallStringExt( x, y, str, colorWhite, false );
    579 
    580 	// draw the cursor
    581 	if ( !showCursor ) {
    582 		return;
    583 	}
    584 
    585 	if ( (int)( idLib::frameNumber >> 4 ) & 1 ) {
    586 		return;		// off blink
    587 	}
    588 
    589 	if ( idKeyInput::GetOverstrikeMode() ) {
    590 		cursorChar = 11;
    591 	} else {
    592 		cursorChar = 10;
    593 	}
    594 
    595 	// Move the cursor back to account for color codes
    596 	for ( int i = 0; i<cursor; i++ ) {
    597 		if ( idStr::IsColor( &str[i] ) ) {
    598 			i++;
    599 			prestep += 2;
    600 		}
    601 	}
    602 
    603 	renderSystem->DrawSmallChar( x + ( cursor - prestep ) * size, y, cursorChar );
    604 }