]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/framework/EditField.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / framework / EditField.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
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( void ) {
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( void ) const {
158         return cursor;
159 }
160
161 /*
162 ===============
163 idEditField::ClearAutoComplete
164 ===============
165 */
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;
171                 }
172         }
173         autoComplete.length = 0;
174         autoComplete.valid = false;
175 }
176
177 /*
178 ===============
179 idEditField::GetAutoCompleteLength
180 ===============
181 */
182 int idEditField::GetAutoCompleteLength( void ) const {
183         return autoComplete.length;
184 }
185
186 /*
187 ===============
188 idEditField::AutoComplete
189 ===============
190 */
191 void idEditField::AutoComplete( void ) {
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_INS ) ) && idKeyInput::IsDown( K_SHIFT ) ) {
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_CTRL ) ) {
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_CTRL ) ) {
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 || ( tolower( key ) == 'a' && idKeyInput::IsDown( K_CTRL ) ) ) {
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 || ( tolower( key ) == 'e' && idKeyInput::IsDown( K_CTRL ) ) ) {
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_ALT && key != K_CTRL && key != K_SHIFT ) {
478                 ClearAutoComplete();
479         }
480 }
481
482 /*
483 ===============
484 idEditField::Paste
485 ===============
486 */
487 void idEditField::Paste( void ) {
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( void ) {
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, const idMaterial *shader ) {
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, shader );
579
580         // draw the cursor
581         if ( !showCursor ) {
582                 return;
583         }
584
585         if ( (int)( com_ticNumber >> 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, shader );
604 }