2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 static autoComplete_t globalAutoComplete;
39 static void FindMatches( const char *s ) {
42 if ( idStr::Icmpn( s, globalAutoComplete.completionString, strlen( globalAutoComplete.completionString ) ) != 0 ) {
45 globalAutoComplete.matchCount++;
46 if ( globalAutoComplete.matchCount == 1 ) {
47 idStr::Copynz( globalAutoComplete.currentMatch, s, sizeof( globalAutoComplete.currentMatch ) );
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;
58 globalAutoComplete.currentMatch[i] = 0;
66 static void FindIndexMatch( const char *s ) {
68 if ( idStr::Icmpn( s, globalAutoComplete.completionString, strlen( globalAutoComplete.completionString ) ) != 0 ) {
72 if( globalAutoComplete.findMatchIndex == globalAutoComplete.matchIndex ) {
73 idStr::Copynz( globalAutoComplete.currentMatch, s, sizeof( globalAutoComplete.currentMatch ) );
76 globalAutoComplete.findMatchIndex++;
84 static void PrintMatches( const char *s ) {
85 if ( idStr::Icmpn( s, globalAutoComplete.currentMatch, strlen( globalAutoComplete.currentMatch ) ) == 0 ) {
86 common->Printf( " %s\n", s );
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 ) );
103 idEditField::idEditField
106 idEditField::idEditField() {
113 idEditField::~idEditField
116 idEditField::~idEditField() {
124 void idEditField::Clear( void ) {
128 autoComplete.length = 0;
129 autoComplete.valid = false;
134 idEditField::SetWidthInChars
137 void idEditField::SetWidthInChars( int w ) {
138 assert( w <= MAX_EDIT_LINE );
144 idEditField::SetCursor
147 void idEditField::SetCursor( int c ) {
148 assert( c <= MAX_EDIT_LINE );
154 idEditField::GetCursor
157 int idEditField::GetCursor( void ) const {
163 idEditField::ClearAutoComplete
166 void idEditField::ClearAutoComplete( void ) {
167 if ( autoComplete.length > 0 && autoComplete.length <= (int) strlen( buffer ) ) {
168 buffer[autoComplete.length] = '\0';
169 if ( cursor > autoComplete.length ) {
170 cursor = autoComplete.length;
173 autoComplete.length = 0;
174 autoComplete.valid = false;
179 idEditField::GetAutoCompleteLength
182 int idEditField::GetAutoCompleteLength( void ) const {
183 return autoComplete.length;
188 idEditField::AutoComplete
191 void idEditField::AutoComplete( void ) {
192 char completionArgString[MAX_EDIT_LINE];
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;
203 if ( strlen( autoComplete.completionString ) == 0 ) {
207 globalAutoComplete = autoComplete;
209 cmdSystem->CommandCompletion( FindMatches );
210 cvarSystem->CommandCompletion( FindMatches );
212 autoComplete = globalAutoComplete;
214 if ( autoComplete.matchCount == 0 ) {
215 return; // no matches
218 // when there's only one match or there's an argument
219 if ( autoComplete.matchCount == 1 || completionArgString[0] != '\0' ) {
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;
226 globalAutoComplete = autoComplete;
228 cmdSystem->ArgCompletion( autoComplete.completionString, FindMatches );
229 cvarSystem->ArgCompletion( autoComplete.completionString, FindMatches );
231 autoComplete = globalAutoComplete;
233 idStr::snPrintf( buffer, sizeof( buffer ), "%s", autoComplete.currentMatch );
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 ) );
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 );
252 autoComplete.length = strlen( buffer );
253 autoComplete.valid = ( autoComplete.matchCount != 1 );
254 SetCursor( autoComplete.length );
256 common->Printf( "]%s\n", buffer );
258 // run through again, printing matches
259 globalAutoComplete = autoComplete;
261 cmdSystem->CommandCompletion( PrintMatches );
262 cmdSystem->ArgCompletion( autoComplete.completionString, PrintMatches );
263 cvarSystem->CommandCompletion( PrintCvarMatches );
264 cvarSystem->ArgCompletion( autoComplete.completionString, PrintMatches );
266 } else if ( autoComplete.matchCount != 1 ) {
268 // get the next match and show instead
269 autoComplete.matchIndex++;
270 if ( autoComplete.matchIndex == autoComplete.matchCount ) {
271 autoComplete.matchIndex = 0;
273 autoComplete.findMatchIndex = 0;
275 globalAutoComplete = autoComplete;
277 cmdSystem->CommandCompletion( FindIndexMatch );
278 cmdSystem->ArgCompletion( autoComplete.completionString, FindIndexMatch );
279 cvarSystem->CommandCompletion( FindIndexMatch );
280 cvarSystem->ArgCompletion( autoComplete.completionString, FindIndexMatch );
282 autoComplete = globalAutoComplete;
285 idStr::snPrintf( buffer, sizeof( buffer ), autoComplete.currentMatch );
286 if ( autoComplete.length > (int)strlen( buffer ) ) {
287 autoComplete.length = strlen( buffer );
289 SetCursor( autoComplete.length );
295 idEditField::CharEvent
298 void idEditField::CharEvent( int ch ) {
301 if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste
306 if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field
311 len = strlen( buffer );
313 if ( ch == 'h' - 'a' + 1 || ch == K_BACKSPACE ) { // ctrl-h is backspace
315 memmove( buffer + cursor - 1, buffer + cursor, len + 1 - cursor );
317 if ( cursor < scroll ) {
324 if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home
330 if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end
332 scroll = cursor - widthInChars;
337 // ignore any other non printable chars
343 if ( idKeyInput::GetOverstrikeMode() ) {
344 if ( cursor == MAX_EDIT_LINE - 1 ) {
349 } else { // insert mode
350 if ( len == MAX_EDIT_LINE - 1 ) {
353 memmove( buffer + cursor + 1, buffer + cursor, len + 1 - cursor );
359 if ( cursor >= widthInChars ) {
363 if ( cursor == len + 1 ) {
370 idEditField::KeyDownEvent
373 void idEditField::KeyDownEvent( int key ) {
376 // shift-insert is paste
377 if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && idKeyInput::IsDown( K_SHIFT ) ) {
383 len = strlen( buffer );
385 if ( key == K_DEL ) {
386 if ( autoComplete.length ) {
388 } else if ( cursor < len ) {
389 memmove( buffer + cursor, buffer + cursor + 1, len - cursor );
394 if ( key == K_RIGHTARROW ) {
395 if ( idKeyInput::IsDown( K_CTRL ) ) {
397 while( ( cursor < len ) && ( buffer[ cursor ] != ' ' ) ) {
401 while( ( cursor < len ) && ( buffer[ cursor ] == ' ' ) ) {
408 if ( cursor > len ) {
412 if ( cursor >= scroll + widthInChars ) {
413 scroll = cursor - widthInChars + 1;
416 if ( autoComplete.length > 0 ) {
417 autoComplete.length = cursor;
422 if ( key == K_LEFTARROW ) {
423 if ( idKeyInput::IsDown( K_CTRL ) ) {
424 // skip to previous word
425 while( ( cursor > 0 ) && ( buffer[ cursor - 1 ] == ' ' ) ) {
429 while( ( cursor > 0 ) && ( buffer[ cursor - 1 ] != ' ' ) ) {
439 if ( cursor < scroll ) {
443 if ( autoComplete.length ) {
444 autoComplete.length = cursor;
449 if ( key == K_HOME || ( tolower( key ) == 'a' && idKeyInput::IsDown( K_CTRL ) ) ) {
452 if ( autoComplete.length ) {
453 autoComplete.length = cursor;
454 autoComplete.valid = false;
459 if ( key == K_END || ( tolower( key ) == 'e' && idKeyInput::IsDown( K_CTRL ) ) ) {
461 if ( cursor >= scroll + widthInChars ) {
462 scroll = cursor - widthInChars + 1;
464 if ( autoComplete.length ) {
465 autoComplete.length = cursor;
466 autoComplete.valid = false;
471 if ( key == K_INS ) {
472 idKeyInput::SetOverstrikeMode( !idKeyInput::GetOverstrikeMode() );
476 // clear autocompletion buffer on normal key input
477 if ( key != K_CAPSLOCK && key != K_ALT && key != K_CTRL && key != K_SHIFT ) {
487 void idEditField::Paste( void ) {
491 cbd = Sys_GetClipboardData();
497 // send as if typed, so insert / overstrike works properly
498 pasteLen = strlen( cbd );
499 for ( i = 0; i < pasteLen; i++ ) {
508 idEditField::GetBuffer
511 char *idEditField::GetBuffer( void ) {
517 idEditField::SetBuffer
520 void idEditField::SetBuffer( const char *buf ) {
522 idStr::Copynz( buffer, buf, sizeof( buffer ) );
523 SetCursor( strlen( buffer ) );
531 void idEditField::Draw( int x, int y, int width, bool showCursor, const idMaterial *shader ) {
536 char str[MAX_EDIT_LINE];
539 size = SMALLCHAR_WIDTH;
541 drawLen = widthInChars;
542 len = strlen( buffer ) + 1;
544 // guarantee that cursor will be visible
545 if ( len <= drawLen ) {
548 if ( scroll + drawLen > len ) {
549 scroll = len - drawLen;
557 if ( idStr::IsColor( buffer + prestep ) ) {
560 if ( prestep > 0 && idStr::IsColor( buffer + prestep - 1 ) ) {
565 if ( prestep + drawLen > len ) {
566 drawLen = len - prestep;
569 // extract <drawLen> characters from the field at <prestep>
570 if ( drawLen >= MAX_EDIT_LINE ) {
571 common->Error( "drawLen >= MAX_EDIT_LINE" );
574 memcpy( str, buffer + prestep, drawLen );
578 renderSystem->DrawSmallStringExt( x, y, str, colorWhite, false, shader );
585 if ( (int)( com_ticNumber >> 4 ) & 1 ) {
589 if ( idKeyInput::GetOverstrikeMode() ) {
595 // Move the cursor back to account for color codes
596 for ( int i = 0; i<cursor; i++ ) {
597 if ( idStr::IsColor( &str[i] ) ) {
603 renderSystem->DrawSmallChar( x + ( cursor - prestep ) * size, y, cursorChar, shader );